kibi-cli 0.4.3 → 0.5.1
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/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +91 -28
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +5 -1
- package/dist/extractors/manifest.d.ts +1 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +73 -67
- package/dist/extractors/markdown.d.ts +1 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +16 -8
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +6 -4
- package/dist/public/schemas/entity.d.ts +3 -0
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +1 -0
- package/dist/schemas/entity.schema.json +1 -0
- package/dist/traceability/symbol-extract.d.ts +8 -1
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +91 -97
- package/dist/traceability/temp-kb.d.ts +2 -0
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +101 -0
- package/package.json +2 -2
- package/src/public/schemas/entity.ts +1 -0
- package/src/schemas/entity.schema.json +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAmDA,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,SAAS,EAAE,CAAC;AAI1B,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;AAmGD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgR/B"}
|
package/dist/commands/check.js
CHANGED
|
@@ -15,21 +15,96 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
+
import { existsSync } from "node:fs";
|
|
18
19
|
import * as path from "node:path";
|
|
19
|
-
import { extractFromManifest } from "../extractors/manifest.js";
|
|
20
|
+
import { extractFromManifest, extractFromManifestString } from "../extractors/manifest.js";
|
|
21
|
+
import { extractFromMarkdownString, } from "../extractors/markdown.js";
|
|
20
22
|
import { PrologProcess } from "../prolog.js";
|
|
21
23
|
import { escapeAtom, parseTriples, parseViolationRows, } from "../prolog/codec.js";
|
|
22
24
|
import { getStagedFiles } from "../traceability/git-staged.js";
|
|
23
25
|
import { validateStagedMarkdown } from "../traceability/markdown-validate.js";
|
|
24
|
-
import { extractSymbolsFromStagedFile, } from "../traceability/symbol-extract.js";
|
|
25
|
-
import { cleanupTempKb, consultOverlay, createOverlayFacts, createTempKb, } from "../traceability/temp-kb.js";
|
|
26
|
+
import { createManifestLookupSentinelKey, extractSymbolsFromStagedFile, } from "../traceability/symbol-extract.js";
|
|
27
|
+
import { cleanupTempKb, consultOverlay, createOverlayFacts, createTempKb, projectStagedEntities, } from "../traceability/temp-kb.js";
|
|
26
28
|
import { formatViolations as formatStagedViolations, validateStagedSymbols, } from "../traceability/validate.js";
|
|
27
29
|
import { loadConfig } from "../utils/config.js";
|
|
28
30
|
import { safeCleanupProlog } from "../utils/prolog-cleanup.js";
|
|
29
31
|
import { RULES, getEffectiveRules, } from "../utils/rule-registry.js";
|
|
30
32
|
import { runAggregatedChecks } from "./aggregated-checks.js";
|
|
31
33
|
import { getCurrentBranch } from "./init-helpers.js";
|
|
32
|
-
|
|
34
|
+
function buildManifestLookup(stagedFiles) {
|
|
35
|
+
const manifestLookup = new Map();
|
|
36
|
+
const manifestResults = [];
|
|
37
|
+
// Pre-populate lookup from working-tree manifests so that code-only changes
|
|
38
|
+
// (where symbols.yaml is not staged) still resolve to the correct symbol IDs
|
|
39
|
+
// and relationships already defined on disk.
|
|
40
|
+
const config = loadConfig(process.cwd());
|
|
41
|
+
const symbolsRelPath = config.paths.symbols;
|
|
42
|
+
if (symbolsRelPath) {
|
|
43
|
+
const absSymbolsPath = path.resolve(process.cwd(), symbolsRelPath);
|
|
44
|
+
if (existsSync(absSymbolsPath)) {
|
|
45
|
+
try {
|
|
46
|
+
const entries = extractFromManifest(absSymbolsPath);
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
const sourceFile = entry.sourceFile || entry.entity.source || absSymbolsPath;
|
|
49
|
+
const key = `${sourceFile}:${entry.entity.title}`;
|
|
50
|
+
manifestLookup.set(key, {
|
|
51
|
+
id: entry.entity.id,
|
|
52
|
+
relationships: entry.relationships
|
|
53
|
+
.filter((relationship) => relationship.type === "implements" ||
|
|
54
|
+
relationship.type === "covered_by")
|
|
55
|
+
.map((relationship) => ({
|
|
56
|
+
type: relationship.type,
|
|
57
|
+
to: relationship.to,
|
|
58
|
+
})),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
// Ignore working-tree manifest parsing errors; staged-only fallback still applies
|
|
64
|
+
if (process.env.KIBI_TRACE || process.env.KIBI_DEBUG) {
|
|
65
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
66
|
+
console.debug(`[kibi] skipping working-tree manifest ${absSymbolsPath}: ${msg}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const stagedManifestFiles = stagedFiles.filter((file) => file.content !== undefined &&
|
|
72
|
+
(file.path.endsWith("/symbols.yaml") ||
|
|
73
|
+
file.path.endsWith("/symbols.yml") ||
|
|
74
|
+
file.path === "symbols.yaml" ||
|
|
75
|
+
file.path === "symbols.yml"));
|
|
76
|
+
for (const manifestFile of stagedManifestFiles) {
|
|
77
|
+
manifestLookup.set(createManifestLookupSentinelKey(manifestFile.path), {
|
|
78
|
+
id: manifestFile.path,
|
|
79
|
+
relationships: [],
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
const entries = extractFromManifestString(manifestFile.content ?? "", manifestFile.path);
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
manifestResults.push({
|
|
85
|
+
entity: entry.entity,
|
|
86
|
+
relationships: entry.relationships,
|
|
87
|
+
});
|
|
88
|
+
const sourceFile = entry.sourceFile || entry.entity.source || manifestFile.path;
|
|
89
|
+
const key = `${sourceFile}:${entry.entity.title}`;
|
|
90
|
+
manifestLookup.set(key, {
|
|
91
|
+
id: entry.entity.id,
|
|
92
|
+
relationships: entry.relationships
|
|
93
|
+
.filter((relationship) => relationship.type === "implements" ||
|
|
94
|
+
relationship.type === "covered_by")
|
|
95
|
+
.map((relationship) => ({
|
|
96
|
+
type: relationship.type,
|
|
97
|
+
to: relationship.to,
|
|
98
|
+
})),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Ignore manifest parsing errors
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { manifestLookup, manifestResults };
|
|
107
|
+
}
|
|
33
108
|
// implements REQ-006
|
|
34
109
|
export async function checkCommand(options) {
|
|
35
110
|
let prolog = null;
|
|
@@ -59,33 +134,12 @@ export async function checkCommand(options) {
|
|
|
59
134
|
const minLinks = options.minLinks ? Number(options.minLinks) : 1;
|
|
60
135
|
let tempCtx = null;
|
|
61
136
|
try {
|
|
62
|
-
const config = loadConfig(process.cwd());
|
|
63
|
-
const manifestLookup = new Map();
|
|
64
|
-
const { manifestFiles } = await discoverSourceFiles(process.cwd(), config.paths);
|
|
65
|
-
for (const manifestPath of manifestFiles) {
|
|
66
|
-
try {
|
|
67
|
-
const entries = extractFromManifest(manifestPath);
|
|
68
|
-
for (const entry of entries) {
|
|
69
|
-
// Prefer the per-symbol sourceFile; fall back to entity.source or manifest path
|
|
70
|
-
const sourceFile = entry.sourceFile || entry.entity.source || manifestPath;
|
|
71
|
-
const key = `${sourceFile}:${entry.entity.title}`;
|
|
72
|
-
// Extract requirement links (implements relationships to REQ-*)
|
|
73
|
-
const links = entry.relationships
|
|
74
|
-
.filter((r) => r.type === "implements" &&
|
|
75
|
-
r.to.match(/^[A-Z][A-Z0-9\-_]*$/))
|
|
76
|
-
.map((r) => r.to);
|
|
77
|
-
manifestLookup.set(key, { id: entry.entity.id, links });
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
// Ignore manifest parsing errors
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
137
|
const stagedFiles = getStagedFiles();
|
|
85
138
|
if (!stagedFiles || stagedFiles.length === 0) {
|
|
86
139
|
console.log("No staged files found.");
|
|
87
140
|
return { exitCode: 0 };
|
|
88
141
|
}
|
|
142
|
+
const { manifestLookup, manifestResults } = buildManifestLookup(stagedFiles);
|
|
89
143
|
const codeFiles = stagedFiles.filter((f) => !f.path.endsWith(".md"));
|
|
90
144
|
const markdownFiles = stagedFiles.filter((f) => f.path.endsWith(".md"));
|
|
91
145
|
const markdownErrors = [];
|
|
@@ -118,8 +172,13 @@ export async function checkCommand(options) {
|
|
|
118
172
|
console.error(`Error extracting symbols from staged file ${f.path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
119
173
|
}
|
|
120
174
|
}
|
|
121
|
-
|
|
122
|
-
|
|
175
|
+
const markdownResults = markdownFiles.map((file) => extractFromMarkdownString(file.content ?? "", file.path));
|
|
176
|
+
const stagedEntityResults = [
|
|
177
|
+
...manifestResults,
|
|
178
|
+
...markdownResults,
|
|
179
|
+
];
|
|
180
|
+
if (allSymbols.length === 0 && stagedEntityResults.length === 0) {
|
|
181
|
+
console.log("No exported symbols or staged entities found in staged files.");
|
|
123
182
|
return { exitCode: 0 };
|
|
124
183
|
}
|
|
125
184
|
if (allSymbols.length === 0) {
|
|
@@ -128,9 +187,13 @@ export async function checkCommand(options) {
|
|
|
128
187
|
}
|
|
129
188
|
// Create temp KB
|
|
130
189
|
tempCtx = await createTempKb(resolvedKbPath);
|
|
190
|
+
if (stagedEntityResults.length > 0) {
|
|
191
|
+
await projectStagedEntities(tempCtx.prolog, stagedEntityResults);
|
|
192
|
+
}
|
|
131
193
|
const overlayFacts = createOverlayFacts(allSymbols);
|
|
132
194
|
const fs = await import("node:fs/promises");
|
|
133
195
|
await fs.writeFile(tempCtx.overlayPath, overlayFacts, "utf8");
|
|
196
|
+
await fs.cp(tempCtx.overlayPath, path.join(tempCtx.kbPath, "changed_symbols.pl"));
|
|
134
197
|
await consultOverlay(tempCtx);
|
|
135
198
|
const violationsRaw = await validateStagedSymbols({
|
|
136
199
|
minLinks,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"AAuFA,wBAAsB,gBAAgB,CACpC,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,IAAI,CAQN;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAMpD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAajD;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAYf;AASD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAmCnE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAiBpD"}
|
|
@@ -24,6 +24,8 @@ const POST_CHECKOUT_HOOK = `#!/bin/sh
|
|
|
24
24
|
# post-checkout hook for kibi
|
|
25
25
|
# Parameters: old_ref new_ref branch_flag
|
|
26
26
|
# branch_flag is 1 for branch checkout, 0 for file checkout
|
|
27
|
+
# Refresh branch/worktree assumptions after checkout so advisory plugin state
|
|
28
|
+
# starts from synced KB data instead of stale in-memory cache assumptions.
|
|
27
29
|
|
|
28
30
|
old_ref=$1
|
|
29
31
|
new_ref=$2
|
|
@@ -44,6 +46,7 @@ fi
|
|
|
44
46
|
const POST_MERGE_HOOK = `#!/bin/sh
|
|
45
47
|
# post-merge hook for kibi
|
|
46
48
|
# Parameter: squash_flag (not used)
|
|
49
|
+
# Refresh KB state after merge so branch-level assumptions remain current.
|
|
47
50
|
|
|
48
51
|
kibi sync
|
|
49
52
|
`;
|
|
@@ -60,7 +63,8 @@ fi
|
|
|
60
63
|
`;
|
|
61
64
|
const PRE_COMMIT_HOOK = `#!/bin/sh
|
|
62
65
|
# pre-commit hook for kibi
|
|
63
|
-
#
|
|
66
|
+
# Hard enforcement boundary: commits are blocked only here via kibi check.
|
|
67
|
+
# The OpenCode plugin remains advisory and must not replace this gate.
|
|
64
68
|
|
|
65
69
|
set -e
|
|
66
70
|
kibi check --staged
|
|
@@ -27,5 +27,6 @@ export declare class ManifestError extends Error {
|
|
|
27
27
|
filePath: string;
|
|
28
28
|
constructor(message: string, filePath: string);
|
|
29
29
|
}
|
|
30
|
+
export declare function extractFromManifestString(content: string, filePath: string): ExtractionResult[];
|
|
30
31
|
export declare function extractFromManifest(filePath: string): ExtractionResult[];
|
|
31
32
|
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/extractors/manifest.ts"],"names":[],"mappings":"AAsBA,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,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;IACvC,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAc,SAAQ,KAAK;IAG7B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAK1B;
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/extractors/manifest.ts"],"names":[],"mappings":"AAsBA,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,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;IACvC,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAc,SAAQ,KAAK;IAG7B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAK1B;AA6GD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,gBAAgB,EAAE,CAmBpB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAGxE"}
|
|
@@ -26,77 +26,79 @@ export class ManifestError extends Error {
|
|
|
26
26
|
this.name = "ManifestError";
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
throw new ManifestError("Missing required field: title", filePath);
|
|
29
|
+
function extractRelationships(id, symbol) {
|
|
30
|
+
const relationships = [];
|
|
31
|
+
if (Array.isArray(symbol.links)) {
|
|
32
|
+
for (const link of symbol.links) {
|
|
33
|
+
if (typeof link === "string") {
|
|
34
|
+
relationships.push({
|
|
35
|
+
type: "implements",
|
|
36
|
+
from: id,
|
|
37
|
+
to: link,
|
|
38
|
+
});
|
|
40
39
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
from: id,
|
|
51
|
-
to: link,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
else if (link !== null && typeof link === "object") {
|
|
55
|
-
const typedLink = link;
|
|
56
|
-
if (typeof typedLink.type === "string" &&
|
|
57
|
-
typeof typedLink.target === "string") {
|
|
58
|
-
relationships.push({
|
|
59
|
-
type: typedLink.type,
|
|
60
|
-
from: id,
|
|
61
|
-
to: typedLink.target,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
40
|
+
else if (link !== null && typeof link === "object") {
|
|
41
|
+
const typedLink = link;
|
|
42
|
+
if (typeof typedLink.type === "string" &&
|
|
43
|
+
typeof typedLink.target === "string") {
|
|
44
|
+
relationships.push({
|
|
45
|
+
type: typedLink.type,
|
|
46
|
+
from: id,
|
|
47
|
+
to: typedLink.target,
|
|
48
|
+
});
|
|
65
49
|
}
|
|
66
50
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(symbol.relationships)) {
|
|
54
|
+
for (const rel of symbol.relationships) {
|
|
55
|
+
if (rel &&
|
|
56
|
+
typeof rel.type === "string" &&
|
|
57
|
+
typeof rel.target === "string") {
|
|
58
|
+
relationships.push({
|
|
59
|
+
type: rel.type,
|
|
60
|
+
from: id,
|
|
61
|
+
to: rel.target,
|
|
62
|
+
});
|
|
80
63
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return relationships;
|
|
67
|
+
}
|
|
68
|
+
function extractFromParsedManifest(manifest, filePath) {
|
|
69
|
+
if (!manifest.symbols || !Array.isArray(manifest.symbols)) {
|
|
70
|
+
throw new ManifestError("No symbols array found in manifest", filePath);
|
|
71
|
+
}
|
|
72
|
+
return manifest.symbols.map((symbol) => {
|
|
73
|
+
if (!symbol.title) {
|
|
74
|
+
throw new ManifestError("Missing required field: title", filePath);
|
|
75
|
+
}
|
|
76
|
+
const id = symbol.id || generateId(filePath, symbol.title);
|
|
77
|
+
return {
|
|
78
|
+
entity: {
|
|
79
|
+
id,
|
|
80
|
+
type: "symbol",
|
|
81
|
+
title: symbol.title,
|
|
82
|
+
status: symbol.status || "active",
|
|
83
|
+
created_at: symbol.created_at || new Date().toISOString(),
|
|
84
|
+
updated_at: symbol.updated_at || new Date().toISOString(),
|
|
85
|
+
source: filePath,
|
|
86
|
+
tags: symbol.tags,
|
|
87
|
+
owner: symbol.owner,
|
|
88
|
+
priority: symbol.priority,
|
|
89
|
+
severity: symbol.severity,
|
|
90
|
+
text_ref: symbol.text_ref,
|
|
91
|
+
},
|
|
92
|
+
relationships: extractRelationships(id, symbol),
|
|
93
|
+
sourceFile: symbol.sourceFile ?? symbol.source,
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// implements REQ-007
|
|
98
|
+
export function extractFromManifestString(content, filePath) {
|
|
99
|
+
try {
|
|
100
|
+
const manifest = parseYAML(content);
|
|
101
|
+
return extractFromParsedManifest(manifest, filePath);
|
|
100
102
|
}
|
|
101
103
|
catch (error) {
|
|
102
104
|
if (error instanceof ManifestError) {
|
|
@@ -108,6 +110,10 @@ export function extractFromManifest(filePath) {
|
|
|
108
110
|
throw error;
|
|
109
111
|
}
|
|
110
112
|
}
|
|
113
|
+
export function extractFromManifest(filePath) {
|
|
114
|
+
const content = readFileSync(filePath, "utf8");
|
|
115
|
+
return extractFromManifestString(content, filePath);
|
|
116
|
+
}
|
|
111
117
|
function generateId(filePath, title) {
|
|
112
118
|
const hash = createHash("sha256");
|
|
113
119
|
hash.update(`${filePath}:${title}`);
|
|
@@ -50,6 +50,7 @@ export declare class FrontmatterError extends Error {
|
|
|
50
50
|
toString(): string;
|
|
51
51
|
}
|
|
52
52
|
export declare function detectEmbeddedEntities(data: Record<string, unknown>, entityType: string): string[];
|
|
53
|
+
export declare function extractFromMarkdownString(content: string, filePath: string): ExtractionResult;
|
|
53
54
|
export declare function extractFromMarkdown(filePath: string): ExtractionResult;
|
|
54
55
|
export declare function inferTypeFromPath(filePath: string): string | null;
|
|
55
56
|
export declare function normalizeDateLike(value: unknown): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"AAmDA,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,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,SAAS,CAAC,EAAE,SAAS,GAAG,gBAAgB,GAAG,aAAa,GAAG,MAAM,CAAC;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IACtD,UAAU,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;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;AAaD,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;AAGD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"AAmDA,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,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,SAAS,CAAC,EAAE,SAAS,GAAG,gBAAgB,GAAG,aAAa,GAAG,MAAM,CAAC;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IACtD,UAAU,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;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;AAaD,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;AA6ND,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAElB;AAGD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAatE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASjE;AASD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQpE"}
|
|
@@ -118,14 +118,7 @@ export function detectEmbeddedEntities(data, entityType) {
|
|
|
118
118
|
return detected;
|
|
119
119
|
}
|
|
120
120
|
// implements REQ-007, REQ-004
|
|
121
|
-
|
|
122
|
-
let content;
|
|
123
|
-
try {
|
|
124
|
-
content = readFileSync(filePath, "utf8");
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
throw new FrontmatterError(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`, filePath, { classification: "File Read Error" });
|
|
128
|
-
}
|
|
121
|
+
function extractFromMarkdownContent(content, filePath) {
|
|
129
122
|
try {
|
|
130
123
|
const { data } = matter(content);
|
|
131
124
|
if (content.trim().startsWith("---")) {
|
|
@@ -292,6 +285,21 @@ export function extractFromMarkdown(filePath) {
|
|
|
292
285
|
throw error;
|
|
293
286
|
}
|
|
294
287
|
}
|
|
288
|
+
// implements REQ-007, REQ-004
|
|
289
|
+
export function extractFromMarkdownString(content, filePath) {
|
|
290
|
+
return extractFromMarkdownContent(content, filePath);
|
|
291
|
+
}
|
|
292
|
+
// implements REQ-007, REQ-004
|
|
293
|
+
export function extractFromMarkdown(filePath) {
|
|
294
|
+
let content;
|
|
295
|
+
try {
|
|
296
|
+
content = readFileSync(filePath, "utf8");
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
throw new FrontmatterError(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`, filePath, { classification: "File Read Error" });
|
|
300
|
+
}
|
|
301
|
+
return extractFromMarkdownContent(content, filePath);
|
|
302
|
+
}
|
|
295
303
|
export function inferTypeFromPath(filePath) {
|
|
296
304
|
if (filePath.includes("/requirements/"))
|
|
297
305
|
return "req";
|
package/dist/prolog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,CAyCxC;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;
|
|
1
|
+
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,CAyCxC;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;YAsCd,YAAY;IA0CpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAuJ1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,kBAAkB;YAIZ,YAAY;IAkC1B,OAAO,CAAC,WAAW;IAsGnB,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
|
@@ -88,6 +88,7 @@ export class PrologProcess {
|
|
|
88
88
|
this.process.stderr.on("data", (chunk) => {
|
|
89
89
|
this.errorBuffer += chunk.toString();
|
|
90
90
|
});
|
|
91
|
+
this.process.stdin.write("true.\n");
|
|
91
92
|
if (!this.onProcessExit) {
|
|
92
93
|
this.onProcessExit = () => {
|
|
93
94
|
void this.terminate();
|
|
@@ -109,13 +110,14 @@ export class PrologProcess {
|
|
|
109
110
|
if (this.errorBuffer.includes("ERROR")) {
|
|
110
111
|
throw new Error(`Failed to load kb module: ${this.translateError(this.errorBuffer)}`);
|
|
111
112
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
if (this.outputBuffer.includes("true.")) {
|
|
114
|
+
this.outputBuffer = "";
|
|
115
|
+
this.errorBuffer = "";
|
|
116
|
+
return;
|
|
115
117
|
}
|
|
116
118
|
// brief pause
|
|
117
119
|
// eslint-disable-next-line no-await-in-loop
|
|
118
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
120
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
119
121
|
}
|
|
120
122
|
// Final sanity check
|
|
121
123
|
if (this.errorBuffer.includes("ERROR")) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/entity.ts"],"names":[],"mappings":"AAwBA,QAAA,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/entity.ts"],"names":[],"mappings":"AAwBA,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqHjB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { HunkRange, StagedFile } from "./git-staged.js";
|
|
2
|
+
type TraceabilityRelationship = {
|
|
3
|
+
type: string;
|
|
4
|
+
to: string;
|
|
5
|
+
};
|
|
2
6
|
export interface ExtractedSymbol {
|
|
3
7
|
id: string;
|
|
4
8
|
name: string;
|
|
@@ -10,11 +14,14 @@ export interface ExtractedSymbol {
|
|
|
10
14
|
};
|
|
11
15
|
hunkRanges: HunkRange[];
|
|
12
16
|
reqLinks: string[];
|
|
17
|
+
relationships?: TraceabilityRelationship[];
|
|
13
18
|
}
|
|
14
19
|
export interface ManifestLookupEntry {
|
|
15
20
|
id: string;
|
|
16
|
-
|
|
21
|
+
relationships?: TraceabilityRelationship[];
|
|
17
22
|
}
|
|
18
23
|
export type ManifestLookup = Map<string, ManifestLookupEntry>;
|
|
24
|
+
export declare function createManifestLookupSentinelKey(manifestPath: string): string;
|
|
19
25
|
export declare function extractSymbolsFromStagedFile(stagedFile: StagedFile, manifestLookup?: ManifestLookup): ExtractedSymbol[];
|
|
26
|
+
export {};
|
|
20
27
|
//# sourceMappingURL=symbol-extract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,KAAK,wBAAwB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAM7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAE9D,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG5E;AAyLD,wBAAgB,4BAA4B,CAE1C,UAAU,EAAE,UAAU,EACtB,cAAc,CAAC,EAAE,cAAc,GAC9B,eAAe,EAAE,CA+JnB"}
|
|
@@ -1,6 +1,93 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { Project, ScriptKind } from "ts-morph";
|
|
3
3
|
import { extractFromManifest } from "../extractors/manifest.js";
|
|
4
|
+
const TRACEABILITY_RELATIONSHIP_TYPES = new Set(["implements", "covered_by"]);
|
|
5
|
+
const REQUIREMENT_ID_PATTERN = /^[A-Z][A-Z0-9\-_]*$/;
|
|
6
|
+
const LOCAL_MANIFEST_NAMES = ["symbols.yaml", "symbols.yml"];
|
|
7
|
+
const MANIFEST_SENTINEL_PREFIX = "__manifest__:";
|
|
8
|
+
export function createManifestLookupSentinelKey(manifestPath) {
|
|
9
|
+
// implements REQ-008
|
|
10
|
+
return `${MANIFEST_SENTINEL_PREFIX}${manifestPath}`;
|
|
11
|
+
}
|
|
12
|
+
function getCandidateManifestPaths(filePath) {
|
|
13
|
+
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
14
|
+
if (!dir) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return LOCAL_MANIFEST_NAMES.map((manifestName) => `${dir}/${manifestName}`);
|
|
18
|
+
}
|
|
19
|
+
function createHashFallbackId(filePath, name) {
|
|
20
|
+
const h = createHash("sha256");
|
|
21
|
+
h.update(`${filePath}:${name}`);
|
|
22
|
+
return h.digest("hex").slice(0, 16);
|
|
23
|
+
}
|
|
24
|
+
function filterTraceabilityRelationships(relationships) {
|
|
25
|
+
if (!relationships?.length) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return relationships.filter((relationship) => TRACEABILITY_RELATIONSHIP_TYPES.has(relationship.type));
|
|
29
|
+
}
|
|
30
|
+
function getRequirementLinks(relationships) {
|
|
31
|
+
return filterTraceabilityRelationships(relationships)
|
|
32
|
+
.filter((relationship) => relationship.type === "implements" &&
|
|
33
|
+
REQUIREMENT_ID_PATTERN.test(relationship.to))
|
|
34
|
+
.map((relationship) => relationship.to);
|
|
35
|
+
}
|
|
36
|
+
function resolveSymbolTraceability(filePath, name, manifestLookup) {
|
|
37
|
+
if (manifestLookup) {
|
|
38
|
+
const lookupKey = `${filePath}:${name}`;
|
|
39
|
+
const entry = manifestLookup.get(lookupKey);
|
|
40
|
+
if (entry) {
|
|
41
|
+
return {
|
|
42
|
+
id: entry.id,
|
|
43
|
+
relationships: filterTraceabilityRelationships(entry.relationships),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const candidateManifestPaths = getCandidateManifestPaths(filePath);
|
|
48
|
+
if (manifestLookup &&
|
|
49
|
+
candidateManifestPaths.some((manifestPath) => manifestLookup.has(createManifestLookupSentinelKey(manifestPath)))) {
|
|
50
|
+
return { id: createHashFallbackId(filePath, name) };
|
|
51
|
+
}
|
|
52
|
+
for (const manifestPath of candidateManifestPaths) {
|
|
53
|
+
try {
|
|
54
|
+
const ents = extractFromManifest(manifestPath);
|
|
55
|
+
for (const e of ents) {
|
|
56
|
+
if (e.entity.title === name) {
|
|
57
|
+
return {
|
|
58
|
+
id: e.entity.id,
|
|
59
|
+
relationships: filterTraceabilityRelationships(e.relationships.map((relationship) => ({
|
|
60
|
+
type: relationship.type,
|
|
61
|
+
to: relationship.to,
|
|
62
|
+
}))),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// ignore - no local manifest or parse error
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { id: createHashFallbackId(filePath, name) };
|
|
72
|
+
}
|
|
73
|
+
function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifestLookup) {
|
|
74
|
+
const { id, relationships } = resolveSymbolTraceability(stagedFile.path, name, manifestLookup);
|
|
75
|
+
const manifestReqLinks = getRequirementLinks(relationships);
|
|
76
|
+
const mergedReqLinks = inlineReqLinks.length > 0 ? inlineReqLinks : manifestReqLinks;
|
|
77
|
+
return {
|
|
78
|
+
id,
|
|
79
|
+
name,
|
|
80
|
+
kind,
|
|
81
|
+
location: {
|
|
82
|
+
file: stagedFile.path,
|
|
83
|
+
startLine: span.startLine,
|
|
84
|
+
endLine: span.endLine,
|
|
85
|
+
},
|
|
86
|
+
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
87
|
+
reqLinks: mergedReqLinks,
|
|
88
|
+
relationships,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
4
91
|
// Simple in-memory cache keyed by blob sha with 30s TTL
|
|
5
92
|
const sourceFileCache = new Map();
|
|
6
93
|
const CACHE_TTL_MS = 30 * 1000;
|
|
@@ -94,21 +181,7 @@ stagedFile, manifestLookup) {
|
|
|
94
181
|
.getJsDocs()
|
|
95
182
|
.map((d) => d.getFullText())
|
|
96
183
|
.join("\n")}`);
|
|
97
|
-
|
|
98
|
-
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
99
|
-
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
100
|
-
results.push({
|
|
101
|
-
id,
|
|
102
|
-
name,
|
|
103
|
-
kind: "function",
|
|
104
|
-
location: {
|
|
105
|
-
file: stagedFile.path,
|
|
106
|
-
startLine: span.startLine,
|
|
107
|
-
endLine: span.endLine,
|
|
108
|
-
},
|
|
109
|
-
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
110
|
-
reqLinks: mergedReqLinks,
|
|
111
|
-
});
|
|
184
|
+
results.push(buildSymbolResult(stagedFile, name, "function", span, reqLinks, manifestLookup));
|
|
112
185
|
}
|
|
113
186
|
catch {
|
|
114
187
|
// skip: individual declaration extraction may fail on malformed AST nodes
|
|
@@ -127,21 +200,7 @@ stagedFile, manifestLookup) {
|
|
|
127
200
|
.getJsDocs()
|
|
128
201
|
.map((d) => d.getFullText())
|
|
129
202
|
.join("\n")}`);
|
|
130
|
-
|
|
131
|
-
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
132
|
-
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
133
|
-
results.push({
|
|
134
|
-
id,
|
|
135
|
-
name,
|
|
136
|
-
kind: "class",
|
|
137
|
-
location: {
|
|
138
|
-
file: stagedFile.path,
|
|
139
|
-
startLine: span.startLine,
|
|
140
|
-
endLine: span.endLine,
|
|
141
|
-
},
|
|
142
|
-
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
143
|
-
reqLinks: mergedReqLinks,
|
|
144
|
-
});
|
|
203
|
+
results.push(buildSymbolResult(stagedFile, name, "class", span, reqLinks, manifestLookup));
|
|
145
204
|
}
|
|
146
205
|
catch {
|
|
147
206
|
// skip: individual declaration extraction may fail on malformed AST nodes
|
|
@@ -157,21 +216,7 @@ stagedFile, manifestLookup) {
|
|
|
157
216
|
const end = en.getEnd();
|
|
158
217
|
const span = getSpan(start, end);
|
|
159
218
|
const reqLinks = parseReqDirectives(en.getText());
|
|
160
|
-
|
|
161
|
-
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
162
|
-
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
163
|
-
results.push({
|
|
164
|
-
id,
|
|
165
|
-
name,
|
|
166
|
-
kind: "enum",
|
|
167
|
-
location: {
|
|
168
|
-
file: stagedFile.path,
|
|
169
|
-
startLine: span.startLine,
|
|
170
|
-
endLine: span.endLine,
|
|
171
|
-
},
|
|
172
|
-
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
173
|
-
reqLinks: mergedReqLinks,
|
|
174
|
-
});
|
|
219
|
+
results.push(buildSymbolResult(stagedFile, name, "enum", span, reqLinks, manifestLookup));
|
|
175
220
|
}
|
|
176
221
|
catch {
|
|
177
222
|
// skip: individual declaration extraction may fail on malformed AST nodes
|
|
@@ -188,21 +233,7 @@ stagedFile, manifestLookup) {
|
|
|
188
233
|
const end = decl.getEnd();
|
|
189
234
|
const span = getSpan(start, end);
|
|
190
235
|
const reqLinks = parseReqDirectives(decl.getText());
|
|
191
|
-
|
|
192
|
-
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
193
|
-
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
194
|
-
results.push({
|
|
195
|
-
id,
|
|
196
|
-
name,
|
|
197
|
-
kind: "variable",
|
|
198
|
-
location: {
|
|
199
|
-
file: stagedFile.path,
|
|
200
|
-
startLine: span.startLine,
|
|
201
|
-
endLine: span.endLine,
|
|
202
|
-
},
|
|
203
|
-
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
204
|
-
reqLinks: mergedReqLinks,
|
|
205
|
-
});
|
|
236
|
+
results.push(buildSymbolResult(stagedFile, name, "variable", span, reqLinks, manifestLookup));
|
|
206
237
|
}
|
|
207
238
|
catch {
|
|
208
239
|
// skip: individual declaration extraction may fail on malformed AST nodes
|
|
@@ -225,40 +256,3 @@ function intersectingHunks(startLine, endLine, hunks) {
|
|
|
225
256
|
}
|
|
226
257
|
return out;
|
|
227
258
|
}
|
|
228
|
-
function resolveSymbolId(filePath, name, manifestLookup) {
|
|
229
|
-
// First, check the provided manifest lookup if available
|
|
230
|
-
if (manifestLookup) {
|
|
231
|
-
// Normalize the source file path for consistent lookup
|
|
232
|
-
const normalizedSource = filePath;
|
|
233
|
-
const lookupKey = `${normalizedSource}:${name}`;
|
|
234
|
-
const entry = manifestLookup.get(lookupKey);
|
|
235
|
-
if (entry) {
|
|
236
|
-
return { id: entry.id, reqLinks: entry.links };
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
// Fallback: attempt to read manifest entries from a local symbols.yaml (best-effort)
|
|
240
|
-
try {
|
|
241
|
-
// Try to find a symbols.yaml in the same directory as the file
|
|
242
|
-
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
243
|
-
if (dir) {
|
|
244
|
-
const manifestPath = `${dir}/symbols.yaml`;
|
|
245
|
-
const ents = extractFromManifest(manifestPath);
|
|
246
|
-
for (const e of ents) {
|
|
247
|
-
if (e.entity.title === name) {
|
|
248
|
-
// Extract requirement links from relationships
|
|
249
|
-
const reqLinks = e.relationships
|
|
250
|
-
.filter((r) => r.type === "implements" && r.to.match(/^[A-Z][A-Z0-9\-_]*$/))
|
|
251
|
-
.map((r) => r.to);
|
|
252
|
-
return { id: e.entity.id, reqLinks };
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
// ignore - no local manifest or parse error
|
|
259
|
-
}
|
|
260
|
-
// Final fallback: deterministic id: sha(file:path:name)
|
|
261
|
-
const h = createHash("sha256");
|
|
262
|
-
h.update(`${filePath}:${name}`);
|
|
263
|
-
return { id: h.digest("hex").slice(0, 16) };
|
|
264
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ExtractionResult } from "../extractors/markdown.js";
|
|
1
2
|
import { PrologProcess } from "../prolog.js";
|
|
2
3
|
import type { ExtractedSymbol } from "./symbol-extract";
|
|
3
4
|
export interface TempKbContext {
|
|
@@ -8,6 +9,7 @@ export interface TempKbContext {
|
|
|
8
9
|
}
|
|
9
10
|
declare function consultOverlay(ctx: TempKbContext): Promise<void>;
|
|
10
11
|
export { consultOverlay };
|
|
12
|
+
export declare function projectStagedEntities(prolog: PrologProcess, results: ExtractionResult[]): Promise<void>;
|
|
11
13
|
export declare function createTempKb(baseKbPath: string): Promise<TempKbContext>;
|
|
12
14
|
export declare function createOverlayFacts(symbols: ExtractedSymbol[]): string;
|
|
13
15
|
export declare function cleanupTempKb(tempDir: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-kb.d.ts","sourceRoot":"","sources":["../../src/traceability/temp-kb.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"temp-kb.d.ts","sourceRoot":"","sources":["../../src/traceability/temp-kb.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AA4ID,iBAAe,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB/D;AAED,OAAO,EAAE,cAAc,EAAE,CAAC;AAG1B,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA2C7E;AAGD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAkBrE;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlE"}
|
|
@@ -3,9 +3,28 @@ import { cp, mkdir, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { PrologProcess } from "../prolog.js";
|
|
6
|
+
import { toPrologAtom, toPrologString } from "../prolog/codec.js";
|
|
6
7
|
const prologByTempDir = new Map();
|
|
7
8
|
const cleanupByTempDir = new Map();
|
|
8
9
|
const cleanedTempDirs = new Set();
|
|
10
|
+
const FACT_ATOM_FIELDS = new Set([
|
|
11
|
+
"fact_kind",
|
|
12
|
+
"operator",
|
|
13
|
+
"value_type",
|
|
14
|
+
"polarity",
|
|
15
|
+
]);
|
|
16
|
+
const FACT_STRING_FIELDS = new Set([
|
|
17
|
+
"subject_key",
|
|
18
|
+
"property_key",
|
|
19
|
+
"value_string",
|
|
20
|
+
"unit",
|
|
21
|
+
"scope",
|
|
22
|
+
"valid_from",
|
|
23
|
+
"valid_to",
|
|
24
|
+
"canonical_key",
|
|
25
|
+
]);
|
|
26
|
+
const FACT_NUMBER_FIELDS = new Set(["value_int", "value_number"]);
|
|
27
|
+
const FACT_BOOLEAN_FIELDS = new Set(["value_bool", "closed_world"]);
|
|
9
28
|
function isTraceEnabled() {
|
|
10
29
|
return Boolean(process.env.KIBI_TRACE || process.env.KIBI_DEBUG);
|
|
11
30
|
}
|
|
@@ -18,6 +37,66 @@ function trace(message) {
|
|
|
18
37
|
function escapePrologAtom(value) {
|
|
19
38
|
return `'${value.replace(/'/g, "''")}'`;
|
|
20
39
|
}
|
|
40
|
+
function serializeTypedFactFields(entity) {
|
|
41
|
+
const fields = [];
|
|
42
|
+
const entityRecord = entity;
|
|
43
|
+
for (const field of FACT_STRING_FIELDS) {
|
|
44
|
+
const value = entityRecord[field];
|
|
45
|
+
if (value !== undefined && value !== null) {
|
|
46
|
+
fields.push(`${field}=${toPrologString(String(value))}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const field of FACT_ATOM_FIELDS) {
|
|
50
|
+
const value = entityRecord[field];
|
|
51
|
+
if (value !== undefined && value !== null) {
|
|
52
|
+
fields.push(`${field}=${toPrologAtom(String(value))}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const field of FACT_NUMBER_FIELDS) {
|
|
56
|
+
const value = entityRecord[field];
|
|
57
|
+
if (value !== undefined && value !== null && typeof value === "number") {
|
|
58
|
+
if (field === "value_int" && !Number.isInteger(value)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
fields.push(`${field}=${value}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const field of FACT_BOOLEAN_FIELDS) {
|
|
65
|
+
const value = entityRecord[field];
|
|
66
|
+
if (value !== undefined && value !== null && typeof value === "boolean") {
|
|
67
|
+
fields.push(`${field}=${value}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return fields;
|
|
71
|
+
}
|
|
72
|
+
function buildEntityAssertionGoal(entity) {
|
|
73
|
+
const props = [
|
|
74
|
+
`id=${toPrologAtom(entity.id)}`,
|
|
75
|
+
`title=${toPrologString(entity.title)}`,
|
|
76
|
+
`status=${toPrologAtom(entity.status)}`,
|
|
77
|
+
`created_at=${toPrologString(entity.created_at)}`,
|
|
78
|
+
`updated_at=${toPrologString(entity.updated_at)}`,
|
|
79
|
+
`source=${toPrologString(entity.source)}`,
|
|
80
|
+
];
|
|
81
|
+
if (entity.tags && entity.tags.length > 0) {
|
|
82
|
+
props.push(`tags=[${entity.tags.map(toPrologAtom).join(",")}]`);
|
|
83
|
+
}
|
|
84
|
+
if (entity.owner)
|
|
85
|
+
props.push(`owner=${toPrologAtom(entity.owner)}`);
|
|
86
|
+
if (entity.priority)
|
|
87
|
+
props.push(`priority=${toPrologAtom(entity.priority)}`);
|
|
88
|
+
if (entity.severity)
|
|
89
|
+
props.push(`severity=${toPrologAtom(entity.severity)}`);
|
|
90
|
+
if (entity.text_ref)
|
|
91
|
+
props.push(`text_ref=${toPrologString(entity.text_ref)}`);
|
|
92
|
+
if (entity.type === "fact") {
|
|
93
|
+
props.push(...serializeTypedFactFields(entity));
|
|
94
|
+
}
|
|
95
|
+
return `kb_assert_entity(${entity.type}, [${props.join(", ")}])`;
|
|
96
|
+
}
|
|
97
|
+
function buildRelationshipAssertionGoal(relationship) {
|
|
98
|
+
return `kb_assert_relationship(${toPrologAtom(relationship.type)}, ${toPrologAtom(relationship.from)}, ${toPrologAtom(relationship.to)}, [])`;
|
|
99
|
+
}
|
|
21
100
|
function createCleanupHandler(tempDir) {
|
|
22
101
|
let inProgress = false;
|
|
23
102
|
return () => {
|
|
@@ -49,12 +128,34 @@ async function consultOverlay(ctx) {
|
|
|
49
128
|
}
|
|
50
129
|
const consultResult = await prolog.query([
|
|
51
130
|
`consult(${escapePrologAtom(ctx.overlayPath)})`,
|
|
131
|
+
"kb_save",
|
|
52
132
|
]);
|
|
53
133
|
if (!consultResult.success) {
|
|
54
134
|
throw new Error(`Failed to consult overlay facts ${ctx.overlayPath}: ${consultResult.error || "unknown error"}`);
|
|
55
135
|
}
|
|
56
136
|
}
|
|
57
137
|
export { consultOverlay };
|
|
138
|
+
// implements REQ-014
|
|
139
|
+
export async function projectStagedEntities(prolog, results) {
|
|
140
|
+
for (const { entity } of results) {
|
|
141
|
+
const retractResult = await prolog.query(`kb_retract_entity(${toPrologAtom(entity.id)})`);
|
|
142
|
+
if (!retractResult.success) {
|
|
143
|
+
throw new Error(`Failed to retract staged entity ${entity.id}: ${retractResult.error || "unknown error"}`);
|
|
144
|
+
}
|
|
145
|
+
const assertEntityResult = await prolog.query(buildEntityAssertionGoal(entity));
|
|
146
|
+
if (!assertEntityResult.success) {
|
|
147
|
+
throw new Error(`Failed to assert staged entity ${entity.id}: ${assertEntityResult.error || "unknown error"}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const { relationships } of results) {
|
|
151
|
+
for (const relationship of relationships) {
|
|
152
|
+
const assertRelationshipResult = await prolog.query(buildRelationshipAssertionGoal(relationship));
|
|
153
|
+
if (!assertRelationshipResult.success) {
|
|
154
|
+
throw new Error(`Failed to assert staged relationship ${relationship.type} ${relationship.from} -> ${relationship.to}: ${assertRelationshipResult.error || "unknown error"}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
58
159
|
export async function createTempKb(baseKbPath) {
|
|
59
160
|
if (!existsSync(baseKbPath)) {
|
|
60
161
|
throw new Error(`Base KB path does not exist: ${baseKbPath}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"fast-glob": "^3.2.12",
|
|
77
77
|
"gray-matter": "^4.0.3",
|
|
78
78
|
"js-yaml": "^4.1.0",
|
|
79
|
-
"kibi-core": "^0.
|
|
79
|
+
"kibi-core": "^0.4.1",
|
|
80
80
|
"ts-morph": "^23.0.0"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|