kibi-cli 2.26.2 → 2.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +24 -0
- package/dist/commands/sync/cache.d.ts +21 -0
- package/dist/commands/sync/cache.d.ts.map +1 -1
- package/dist/commands/sync/cache.js +40 -15
- package/dist/commands/sync/persistence.d.ts +10 -0
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +24 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +26 -11
- package/dist/diagnostics.d.ts +6 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +25 -9
- package/dist/intent-search.d.ts +2 -0
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +5 -0
- package/dist/operations/bootstrap/activation.d.ts +6 -0
- package/dist/operations/bootstrap/activation.d.ts.map +1 -1
- package/dist/operations/bootstrap/activation.js +22 -0
- package/dist/operations/bootstrap/claim-restatement.d.ts +2 -0
- package/dist/operations/bootstrap/claim-restatement.d.ts.map +1 -1
- package/dist/operations/bootstrap/conflict-review.d.ts +16 -0
- package/dist/operations/bootstrap/conflict-review.d.ts.map +1 -0
- package/dist/operations/bootstrap/conflict-review.js +59 -0
- package/dist/operations/bootstrap/generate.d.ts.map +1 -1
- package/dist/operations/bootstrap/generate.js +17 -4
- package/dist/operations/bootstrap/types.d.ts +5 -0
- package/dist/operations/bootstrap/types.d.ts.map +1 -1
- package/dist/operations/migration/entity-type.d.ts +35 -0
- package/dist/operations/migration/entity-type.d.ts.map +1 -0
- package/dist/operations/migration/entity-type.js +89 -0
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +32 -0
- package/dist/operations/mutation/requirement-semantics.d.ts +5 -0
- package/dist/operations/mutation/requirement-semantics.d.ts.map +1 -1
- package/dist/operations/mutation/requirement-semantics.js +24 -5
- package/dist/operations/mutation/stored-entity.d.ts +7 -0
- package/dist/operations/mutation/stored-entity.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-granularity.d.ts +1 -1
- package/dist/operations/mutation/symbol-granularity.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-granularity.js +37 -7
- package/dist/operations/mutation/uncompiled-source.d.ts +6 -0
- package/dist/operations/mutation/uncompiled-source.d.ts.map +1 -1
- package/dist/operations/mutation/uncompiled-source.js +17 -1
- package/dist/operations/mutation/upsert.d.ts +9 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +43 -7
- package/dist/operations/mutation/validation.d.ts.map +1 -1
- package/dist/operations/mutation/validation.js +21 -0
- package/dist/operations/planning/compile-intent.d.ts +16 -0
- package/dist/operations/planning/compile-intent.d.ts.map +1 -1
- package/dist/operations/planning/compile-intent.js +118 -5
- package/dist/operations/planning/conflict-review.d.ts +69 -0
- package/dist/operations/planning/conflict-review.d.ts.map +1 -0
- package/dist/operations/planning/conflict-review.js +133 -0
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +8 -0
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/ingestion-boundary.js +43 -2
- package/dist/prolog/utf8-transport.d.ts +21 -0
- package/dist/prolog/utf8-transport.d.ts.map +1 -0
- package/dist/prolog/utf8-transport.js +49 -0
- package/dist/prolog.d.ts +2 -0
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +19 -10
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +26 -2
- package/dist/public/impact/requirement-statements.d.ts +29 -0
- package/dist/public/impact/requirement-statements.d.ts.map +1 -0
- package/dist/public/impact/requirement-statements.js +127 -0
- package/dist/public/operations/catalog.d.ts +26 -2
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-format-shared.d.ts +1 -1
- package/dist/public/operations/check-format-shared.d.ts.map +1 -1
- package/dist/public/operations/check-format-shared.js +19 -4
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +20 -13
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +7 -2
- package/dist/public/operations/result-envelope.d.ts +2 -0
- package/dist/public/operations/result-envelope.d.ts.map +1 -1
- package/dist/public/operations/result-envelope.js +21 -9
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/check.js +1 -1
- package/dist/public/operations/specs/planning.d.ts +25 -1
- package/dist/public/operations/specs/planning.d.ts.map +1 -1
- package/dist/public/operations/specs/planning.js +16 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/dist/public/skills/kibi-usage/SKILL.md +11 -1
- package/dist/traceability/staged-diagnostics.d.ts +1 -1
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
- package/dist/utils/config.d.ts +8 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +10 -0
- package/package.json +2 -2
- package/src/public/impact/analyzer.ts +38 -2
- package/src/public/impact/requirement-statements.ts +171 -0
- package/src/public/operations/check-format-shared.ts +27 -4
- package/src/public/operations/discovery-executors.ts +20 -13
- package/src/public/operations/migration-plan.ts +12 -4
- package/src/public/operations/result-envelope.ts +18 -4
- package/src/public/operations/specs/check.ts +1 -1
- package/src/public/operations/specs/planning.ts +17 -1
- package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/src/public/skills/kibi-usage/SKILL.md +11 -1
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { intentTokens } from "../../intent-search.js";
|
|
2
|
+
import { escapeAtomContent, parseListOfLists, parsePrologValue, } from "../../prolog/codec.js";
|
|
3
|
+
import { loadEntityRows } from "../../public/operations/discovery-entities.js";
|
|
4
|
+
/**
|
|
5
|
+
* Relationships that give a requirement a logical representation the
|
|
6
|
+
* contradiction checker can compare. A requirement with none of them is prose
|
|
7
|
+
* only: no witness can ever name it.
|
|
8
|
+
*/
|
|
9
|
+
const LOGICAL_RELATIONSHIPS = [
|
|
10
|
+
"constrains",
|
|
11
|
+
"requires_property",
|
|
12
|
+
"requires_predicate",
|
|
13
|
+
"requires_rule",
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Relevance floor for a related requirement, below the kb_search default
|
|
17
|
+
* (0.18): a missed contradiction costs more than one extra comparison, and a
|
|
18
|
+
* requirement sharing half the intent's terms in other words ("label length
|
|
19
|
+
* is limited" against "label length must be at most 200") scores about 0.13.
|
|
20
|
+
* The candidate cap bounds the noise.
|
|
21
|
+
*/
|
|
22
|
+
// implements REQ-kibi-contradiction-review-candidates
|
|
23
|
+
export const REVIEW_CANDIDATE_MIN_SCORE = 0.1;
|
|
24
|
+
/** At most this many related requirements are put up for review. */
|
|
25
|
+
// implements REQ-kibi-contradiction-review-candidates
|
|
26
|
+
export const MAX_REVIEW_CANDIDATES = 5;
|
|
27
|
+
/** The longest requirement statement a review candidate quotes. */
|
|
28
|
+
const MAX_STATEMENT_LENGTH = 600;
|
|
29
|
+
function text(value) {
|
|
30
|
+
return typeof value === "string" ? value.trim() : "";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The current requirements among `ids`, each with whether it has a logical
|
|
34
|
+
* representation. Ids that are not current requirements (superseded,
|
|
35
|
+
* deprecated or unknown) are left out.
|
|
36
|
+
*/
|
|
37
|
+
// implements REQ-kibi-contradiction-review-candidates
|
|
38
|
+
export async function loadReviewRequirements(prolog, ids) {
|
|
39
|
+
const unique = [...new Set(ids.filter(Boolean))];
|
|
40
|
+
const found = new Map();
|
|
41
|
+
if (unique.length === 0)
|
|
42
|
+
return found;
|
|
43
|
+
const idList = unique.map((id) => `'${escapeAtomContent(id)}'`).join(",");
|
|
44
|
+
const result = await prolog.query(`findall([R,M], (member(R, [${idList}]), kb:current_req(R), (once((member(T, [${LOGICAL_RELATIONSHIPS.join(",")}]), kb_relationship(T, R, _))) -> M = modeled ; M = unmodeled)), Rows)`);
|
|
45
|
+
if (!result.success)
|
|
46
|
+
return found;
|
|
47
|
+
const modeled = new Map(parseListOfLists(result.bindings.Rows ?? "[]").map(([id, flag]) => [
|
|
48
|
+
atom(id),
|
|
49
|
+
atom(flag) === "modeled",
|
|
50
|
+
]));
|
|
51
|
+
const rows = await loadEntityRows(prolog, [...modeled.keys()], ["id", "title", "semantic_text"]);
|
|
52
|
+
for (const row of rows) {
|
|
53
|
+
const id = text(row.id);
|
|
54
|
+
if (!modeled.has(id))
|
|
55
|
+
continue;
|
|
56
|
+
found.set(id, {
|
|
57
|
+
id,
|
|
58
|
+
title: text(row.title),
|
|
59
|
+
semanticText: text(row.semantic_text),
|
|
60
|
+
modeled: modeled.get(id) === true,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
function atom(value) {
|
|
66
|
+
return String(parsePrologValue(value ?? "")).replace(/^['"]|['"]$/g, "");
|
|
67
|
+
}
|
|
68
|
+
function sentences(value) {
|
|
69
|
+
return value
|
|
70
|
+
.split(/(?<=[.!?])\s+|\n+/)
|
|
71
|
+
.map((sentence) => sentence.trim())
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The sentence of a requirement most likely to bear on the intent: the one
|
|
76
|
+
* sharing the most intent terms, the first on a tie, the title when the
|
|
77
|
+
* requirement has no prose.
|
|
78
|
+
*/
|
|
79
|
+
// implements REQ-kibi-contradiction-review-candidates
|
|
80
|
+
export function reviewSentence(intent, requirement) {
|
|
81
|
+
const units = sentences(requirement.semanticText);
|
|
82
|
+
if (units.length === 0)
|
|
83
|
+
return requirement.title;
|
|
84
|
+
const wanted = new Set(intentTokens(intent));
|
|
85
|
+
let best = units[0] ?? requirement.title;
|
|
86
|
+
let bestShared = -1;
|
|
87
|
+
for (const unit of units) {
|
|
88
|
+
const shared = intentTokens(unit).filter((token) => wanted.has(token)).length;
|
|
89
|
+
if (shared > bestShared) {
|
|
90
|
+
best = unit;
|
|
91
|
+
bestShared = shared;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return best;
|
|
95
|
+
}
|
|
96
|
+
function bounded(value) {
|
|
97
|
+
return value.length > MAX_STATEMENT_LENGTH
|
|
98
|
+
? `${value.slice(0, MAX_STATEMENT_LENGTH - 1).trimEnd()}…`
|
|
99
|
+
: value;
|
|
100
|
+
}
|
|
101
|
+
// implements REQ-kibi-contradiction-review-candidates
|
|
102
|
+
export function reviewCandidate(intent, requirement, score, decision) {
|
|
103
|
+
return {
|
|
104
|
+
requirementId: requirement.id,
|
|
105
|
+
title: requirement.title,
|
|
106
|
+
sentence: reviewSentence(intent, requirement),
|
|
107
|
+
statement: bounded(requirement.semanticText || requirement.title),
|
|
108
|
+
score: Math.round(score * 1000) / 1000,
|
|
109
|
+
modeled: requirement.modeled,
|
|
110
|
+
review: decision?.verdict ?? "pending",
|
|
111
|
+
...(decision?.note ? { note: decision.note } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* What the agent does next while the contradiction outcome is not decided:
|
|
116
|
+
* resolve a conflict it found, compare the candidates still pending, or model
|
|
117
|
+
* the claims Kibi could not check.
|
|
118
|
+
*/
|
|
119
|
+
// implements REQ-kibi-contradiction-review-candidates
|
|
120
|
+
export function reviewNextAction(candidates, unmodeledClaims) {
|
|
121
|
+
const ids = (review) => candidates
|
|
122
|
+
.filter((candidate) => candidate.review === review)
|
|
123
|
+
.map((candidate) => candidate.requirementId);
|
|
124
|
+
const conflicts = ids("conflict");
|
|
125
|
+
if (conflicts.length > 0)
|
|
126
|
+
return `You judged that the intent contradicts ${conflicts.join(", ")}. Ask the human which rule holds, then supersede or change the conflicting requirement, or change the intent, and compile again. Record a conflict the human has not decided yet as a review:conflict observation fact.`;
|
|
127
|
+
const pending = ids("pending");
|
|
128
|
+
if (pending.length > 0)
|
|
129
|
+
return `Kibi could not compare this intent with ${pending.join(", ")} logically. Read each reviewCandidates entry (its statement and sentence) and decide whether the intent contradicts it, then call kb_compile_intent again with the same arguments plus conflictReviews: [{requirementId, verdict: "conflict" | "no_conflict", note}] for each.`;
|
|
130
|
+
if (unmodeledClaims.length > 0)
|
|
131
|
+
return `Kibi could not check claim ${unmodeledClaims.join(", ")} for contradictions because it has no logical model: model it with kb_model (mode requirement or predicates) and compile again. Until then no conflict is confirmed, only reviewed.`;
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
@@ -12,6 +12,14 @@ export type SemanticRelationship = Readonly<{
|
|
|
12
12
|
to?: unknown;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function validateSemanticInventoryBoundary(payload: Payload, relationships: readonly SemanticRelationship[], receipt: SemanticAdvisorReceipt): SemanticInventoryBoundaryResult;
|
|
15
|
+
/**
|
|
16
|
+
* The inventory contract the advisor derives for this payload's prose, as
|
|
17
|
+
* the properties to resend: the version, source field and hash, one ledger
|
|
18
|
+
* entry per proposition and the assertive claim keys. An entry keeps the
|
|
19
|
+
* status the payload already gave the same claim key; otherwise it carries
|
|
20
|
+
* the advisor's status (an ambiguity or ontology gap stays explicit).
|
|
21
|
+
*/
|
|
22
|
+
export declare function semanticInventoryContract(payload: Payload, receipt: SemanticAdvisorReceipt): Record<string, unknown>;
|
|
15
23
|
export declare function assertSemanticInventoryBoundary(payload: Payload, relationships: readonly SemanticRelationship[], receipt: SemanticAdvisorReceipt): void;
|
|
16
24
|
/**
|
|
17
25
|
* The write-time claim_key check, run against planned writes only. Returns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ingestion-boundary.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/ingestion-boundary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAG3E,OAAO,EACL,KAAK,OAAO,EAOb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAuB,MAAM,YAAY,CAAC;AAgB9E,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,OAAO,CAAC;CACd,CAAC,CAAC;AAuEH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,+BAA+B,CA0IjC;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,IAAI,CAWN;
|
|
1
|
+
{"version":3,"file":"ingestion-boundary.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/ingestion-boundary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAG3E,OAAO,EACL,KAAK,OAAO,EAOb,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAuB,MAAM,YAAY,CAAC;AAgB9E,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,OAAO,CAAC;CACd,CAAC,CAAC;AAuEH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,+BAA+B,CA0IjC;AAED;;;;;;GAMG;AAEH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,sBAAsB,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuBzB;AAGD,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,sBAAsB,GAC9B,IAAI,CAWN;AAwFD;;;;GAIG;AAEH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAC9D,MAAM,GAAG,IAAI,CAUf;AAGD,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,SAAS,oBAAoB,EAAE,EAC9C,MAAM,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE,CAAC,GACD,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
|
@@ -157,10 +157,41 @@ export function validateSemanticInventoryBoundary(payload, relationships, receip
|
|
|
157
157
|
}
|
|
158
158
|
return { applicable, errors, sourceHash };
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* The inventory contract the advisor derives for this payload's prose, as
|
|
162
|
+
* the properties to resend: the version, source field and hash, one ledger
|
|
163
|
+
* entry per proposition and the assertive claim keys. An entry keeps the
|
|
164
|
+
* status the payload already gave the same claim key; otherwise it carries
|
|
165
|
+
* the advisor's status (an ambiguity or ontology gap stays explicit).
|
|
166
|
+
*/
|
|
167
|
+
// implements REQ-kb-upsert-ledger-contract-in-error
|
|
168
|
+
export function semanticInventoryContract(payload, receipt) {
|
|
169
|
+
const semanticSource = semanticSourceOf(payload);
|
|
170
|
+
const sent = new Map(inventoryOf(payload).map((entry) => [
|
|
171
|
+
stringValue(entry.claim_key),
|
|
172
|
+
stringValue(entry.status),
|
|
173
|
+
]));
|
|
174
|
+
return {
|
|
175
|
+
semantic_inventory_version: SEMANTIC_INVENTORY_VERSION,
|
|
176
|
+
semantic_source_field: semanticSource.field,
|
|
177
|
+
semantic_source_hash: semanticSourceHash(semanticSource.text),
|
|
178
|
+
semantic_inventory: receipt.propositions.map((proposition) => ({
|
|
179
|
+
claim_key: proposition.claim_key,
|
|
180
|
+
claim_text: proposition.claim_text,
|
|
181
|
+
role: proposition.role,
|
|
182
|
+
status: sent.get(proposition.claim_key) || proposition.status,
|
|
183
|
+
span: { start: proposition.span.start, end: proposition.span.end },
|
|
184
|
+
})),
|
|
185
|
+
logic_claims: receipt.propositions
|
|
186
|
+
.filter(propositionIsAssertive)
|
|
187
|
+
.map(({ claim_key }) => claim_key),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// implements REQ-kibi-proposition-complete-ingestion, REQ-kb-upsert-ledger-contract-in-error
|
|
160
191
|
export function assertSemanticInventoryBoundary(payload, relationships, receipt) {
|
|
161
192
|
const result = validateSemanticInventoryBoundary(payload, relationships, receipt);
|
|
162
193
|
if (result.errors.length > 0) {
|
|
163
|
-
throw new Error(`Proposition-complete ingestion failed: ${result.errors.join("; ")}.
|
|
194
|
+
throw new Error(`Proposition-complete ingestion failed: ${result.errors.join("; ")}. The contract kb_model mode analyze returns for this prose, to merge into properties and resend (a claim marked modeled needs one requires_property, requires_predicate or requires_rule link to a fact with its claim_key; otherwise keep the advisor's status): ${JSON.stringify(semanticInventoryContract(payload, receipt))}`);
|
|
164
195
|
}
|
|
165
196
|
}
|
|
166
197
|
function modeledClaimKeysOf(payload) {
|
|
@@ -182,10 +213,13 @@ function groundingTargets(payload, relationships) {
|
|
|
182
213
|
function plannedClaimKey(planned) {
|
|
183
214
|
return planned.type === "fact" ? stringValue(planned.claim_key) : "";
|
|
184
215
|
}
|
|
216
|
+
function missingGroundingFactError(target) {
|
|
217
|
+
return `Proposition-complete ingestion failed: logical grounding target '${target}' does not exist yet. Write the fact first: the kb_compile_intent plan has an earlier fact step for '${target}' that carries its claim_key; run that step before this one, or apply the whole plan with kb_apply_plan.`;
|
|
218
|
+
}
|
|
185
219
|
function groundingClaimKeyError(modeledClaimKeys, grounded) {
|
|
186
220
|
const missing = grounded.find(([, claimKey]) => !claimKey);
|
|
187
221
|
if (missing)
|
|
188
|
-
return `Proposition-complete ingestion failed: logical grounding target '${missing[0]}' must declare a claim_key.`;
|
|
222
|
+
return `Proposition-complete ingestion failed: logical grounding target '${missing[0]}' must declare a claim_key: it is a fact without the claim_key of the claim it grounds. Set the claim_key the kb_compile_intent plan's fact step gives it.`;
|
|
189
223
|
const groundedClaimKeys = grounded.map(([, claimKey]) => claimKey);
|
|
190
224
|
const duplicate = groundedClaimKeys.find((claimKey, index) => groundedClaimKeys.indexOf(claimKey) !== index);
|
|
191
225
|
if (duplicate)
|
|
@@ -256,6 +290,13 @@ export async function assertLogicalGroundingClaimKeys(prolog, payload, relations
|
|
|
256
290
|
claimKey = result.success
|
|
257
291
|
? stringValue(result.bindings.ClaimKey).replace(/^['"]|['"]$/g, "")
|
|
258
292
|
: "";
|
|
293
|
+
// implements REQ-kibi-symbol-link-guidance
|
|
294
|
+
// A target that is not stored yet is a skipped plan step, not a fact
|
|
295
|
+
// missing its key: say which step to run first.
|
|
296
|
+
if (!claimKey &&
|
|
297
|
+
!(await prolog.query(`once(kb_entity('${escapeAtom(target)}', _, _))`))
|
|
298
|
+
.success)
|
|
299
|
+
throw new Error(missingGroundingFactError(target));
|
|
259
300
|
}
|
|
260
301
|
grounded.push([target, claimKey]);
|
|
261
302
|
// The first target without a claim_key is reported; later reads are moot.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prolog goal that switches the child's standard streams and its default
|
|
3
|
+
* file encoding to UTF-8. SWI-Prolog otherwise follows the process locale,
|
|
4
|
+
* and under a POSIX locale every non-ASCII byte read from stdin becomes
|
|
5
|
+
* U+FFFD, which the store then persists.
|
|
6
|
+
*/
|
|
7
|
+
export declare const PROLOG_UTF8_STREAMS_GOAL = "set_prolog_flag(encoding, utf8), set_stream(user_input, encoding(utf8)), set_stream(user_output, encoding(utf8)), set_stream(user_error, encoding(utf8))";
|
|
8
|
+
/**
|
|
9
|
+
* Encode a goal for an environment variable. SWI-Prolog decodes environment
|
|
10
|
+
* values with the process locale, so non-ASCII goal text travels as base64
|
|
11
|
+
* of its UTF-8 bytes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function encodeGoalForEnv(goal: string): string;
|
|
14
|
+
/** Prolog goal that binds `Atom` to the goal stored by `encodeGoalForEnv`. */
|
|
15
|
+
export declare function decodeGoalFromEnvGoal(variable: string, atom: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* A UTF-8 decoder for one child stream that keeps a character split across
|
|
18
|
+
* two chunks whole instead of decoding each half to U+FFFD.
|
|
19
|
+
*/
|
|
20
|
+
export declare function utf8ChunkDecoder(): (chunk: Buffer) => string;
|
|
21
|
+
//# sourceMappingURL=utf8-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utf8-transport.d.ts","sourceRoot":"","sources":["../../src/prolog/utf8-transport.ts"],"names":[],"mappings":"AAqBA;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,6JACuH,CAAC;AAG7J;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAGD,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5E;AAGD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAG5D"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { StringDecoder } from "node:string_decoder";
|
|
19
|
+
// implements REQ-prolog-utf8-text
|
|
20
|
+
/**
|
|
21
|
+
* Prolog goal that switches the child's standard streams and its default
|
|
22
|
+
* file encoding to UTF-8. SWI-Prolog otherwise follows the process locale,
|
|
23
|
+
* and under a POSIX locale every non-ASCII byte read from stdin becomes
|
|
24
|
+
* U+FFFD, which the store then persists.
|
|
25
|
+
*/
|
|
26
|
+
export const PROLOG_UTF8_STREAMS_GOAL = "set_prolog_flag(encoding, utf8), set_stream(user_input, encoding(utf8)), set_stream(user_output, encoding(utf8)), set_stream(user_error, encoding(utf8))";
|
|
27
|
+
// implements REQ-prolog-utf8-text
|
|
28
|
+
/**
|
|
29
|
+
* Encode a goal for an environment variable. SWI-Prolog decodes environment
|
|
30
|
+
* values with the process locale, so non-ASCII goal text travels as base64
|
|
31
|
+
* of its UTF-8 bytes.
|
|
32
|
+
*/
|
|
33
|
+
export function encodeGoalForEnv(goal) {
|
|
34
|
+
return Buffer.from(goal, "utf8").toString("base64");
|
|
35
|
+
}
|
|
36
|
+
// implements REQ-prolog-utf8-text
|
|
37
|
+
/** Prolog goal that binds `Atom` to the goal stored by `encodeGoalForEnv`. */
|
|
38
|
+
export function decodeGoalFromEnvGoal(variable, atom) {
|
|
39
|
+
return `getenv('${variable}', ${atom}Encoded), base64_encoded(${atom}, ${atom}Encoded, [encoding(utf8), as(atom)])`;
|
|
40
|
+
}
|
|
41
|
+
// implements REQ-prolog-utf8-text
|
|
42
|
+
/**
|
|
43
|
+
* A UTF-8 decoder for one child stream that keeps a character split across
|
|
44
|
+
* two chunks whole instead of decoding each half to U+FFFD.
|
|
45
|
+
*/
|
|
46
|
+
export function utf8ChunkDecoder() {
|
|
47
|
+
const decoder = new StringDecoder("utf8");
|
|
48
|
+
return (chunk) => decoder.write(chunk);
|
|
49
|
+
}
|
package/dist/prolog.d.ts
CHANGED
package/dist/prolog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA2BA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"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"}
|
package/dist/prolog.js
CHANGED
|
@@ -26,6 +26,7 @@ import { kibiPackageVersions } from "./package-versions.js";
|
|
|
26
26
|
import { writePerformanceTraceEvent } from "./performance-trace.js";
|
|
27
27
|
import { extractPrologErrorRecord, } from "./prolog/error-terms.js";
|
|
28
28
|
import { resolveSwipl, swiplChildEnv, } from "./prolog/swipl-resolver.js";
|
|
29
|
+
import { PROLOG_UTF8_STREAMS_GOAL, decodeGoalFromEnvGoal, encodeGoalForEnv, utf8ChunkDecoder, } from "./prolog/utf8-transport.js";
|
|
29
30
|
const importMetaDir = path.dirname(fileURLToPath(import.meta.url));
|
|
30
31
|
const DEFAULT_PROLOG_OUTPUT_MAX_BUFFER_BYTES = 8 * 1024 * 1024;
|
|
31
32
|
const PROLOG_OUTPUT_OVERFLOW_ERROR = "Query exceeded bounded Prolog output capacity (ENOBUFS); narrow the operation or reduce stored entity size";
|
|
@@ -106,6 +107,8 @@ export class PrologProcess {
|
|
|
106
107
|
outputOverflowed = false;
|
|
107
108
|
errorBuffer = "";
|
|
108
109
|
errorBufferBytes = 0;
|
|
110
|
+
decodeOutput = utf8ChunkDecoder();
|
|
111
|
+
decodeError = utf8ChunkDecoder();
|
|
109
112
|
cache = new Map();
|
|
110
113
|
interactiveQueryTail = Promise.resolve();
|
|
111
114
|
terminationPromise = null;
|
|
@@ -183,7 +186,7 @@ export class PrologProcess {
|
|
|
183
186
|
this.invalidateCache();
|
|
184
187
|
this.process = spawn(launch.bin, [
|
|
185
188
|
"-g",
|
|
186
|
-
|
|
189
|
+
`${PROLOG_UTF8_STREAMS_GOAL}, use_module('${kbPath}'), use_module(library(semweb/rdf_db)), set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])`,
|
|
187
190
|
"--quiet",
|
|
188
191
|
], {
|
|
189
192
|
detached: process.platform !== "win32",
|
|
@@ -196,6 +199,8 @@ export class PrologProcess {
|
|
|
196
199
|
},
|
|
197
200
|
});
|
|
198
201
|
this.clearQueryBuffers();
|
|
202
|
+
this.decodeOutput = utf8ChunkDecoder();
|
|
203
|
+
this.decodeError = utf8ChunkDecoder();
|
|
199
204
|
if (!this.process.stdout || !this.process.stderr || !this.process.stdin) {
|
|
200
205
|
throw new Error("Failed to spawn Prolog process");
|
|
201
206
|
}
|
|
@@ -574,10 +579,11 @@ export class PrologProcess {
|
|
|
574
579
|
: `(${goalList.join(", ")})`;
|
|
575
580
|
const kbModulePath = resolveKbPlPath();
|
|
576
581
|
const prologGoal = [
|
|
582
|
+
PROLOG_UTF8_STREAMS_GOAL,
|
|
577
583
|
`use_module('${kbModulePath}')`,
|
|
578
584
|
"use_module(library(semweb/rdf_db))",
|
|
579
585
|
"set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])",
|
|
580
|
-
"
|
|
586
|
+
decodeGoalFromEnvGoal("KIBI_GOAL", "GoalAtom"),
|
|
581
587
|
"read_term_from_atom(GoalAtom, Goal, [variable_names(Vars)])",
|
|
582
588
|
kbPath ? "getenv('KIBI_KB_PATH', KBPath), kb_attach(KBPath)" : "true",
|
|
583
589
|
isBatch ? "WrappedGoal = rdf_transaction(Goal)" : "WrappedGoal = Goal",
|
|
@@ -601,7 +607,7 @@ export class PrologProcess {
|
|
|
601
607
|
env: {
|
|
602
608
|
...process.env,
|
|
603
609
|
...launch.env,
|
|
604
|
-
KIBI_GOAL: combinedGoal,
|
|
610
|
+
KIBI_GOAL: encodeGoalForEnv(combinedGoal ?? "true"),
|
|
605
611
|
KIBI_RUNTIME_NAME: runtimeName,
|
|
606
612
|
KIBI_RUNTIME_VERSION: runtimeVersion,
|
|
607
613
|
KIBI_PACKAGE_VERSIONS: packageVersions,
|
|
@@ -627,13 +633,16 @@ export class PrologProcess {
|
|
|
627
633
|
let settled = false;
|
|
628
634
|
let closed = false;
|
|
629
635
|
let termination = null;
|
|
636
|
+
const decodeStdout = utf8ChunkDecoder();
|
|
637
|
+
const decodeStderr = utf8ChunkDecoder();
|
|
630
638
|
const appendChunk = (chunk, target) => {
|
|
631
639
|
if (outputOverflowed)
|
|
632
640
|
return;
|
|
633
641
|
const bytes = target === "stdout" ? outputBytes : errorBytes;
|
|
634
642
|
const remaining = this.maxOutputBytes - bytes;
|
|
643
|
+
const decode = target === "stdout" ? decodeStdout : decodeStderr;
|
|
635
644
|
if (chunk.byteLength > remaining) {
|
|
636
|
-
const clipped = chunk.subarray(0, Math.max(0, remaining))
|
|
645
|
+
const clipped = decode(chunk.subarray(0, Math.max(0, remaining)));
|
|
637
646
|
if (target === "stdout")
|
|
638
647
|
stdout += clipped;
|
|
639
648
|
else
|
|
@@ -643,11 +652,11 @@ export class PrologProcess {
|
|
|
643
652
|
return;
|
|
644
653
|
}
|
|
645
654
|
if (target === "stdout") {
|
|
646
|
-
stdout += chunk
|
|
655
|
+
stdout += decode(chunk);
|
|
647
656
|
outputBytes += chunk.byteLength;
|
|
648
657
|
}
|
|
649
658
|
else {
|
|
650
|
-
stderr += chunk
|
|
659
|
+
stderr += decode(chunk);
|
|
651
660
|
errorBytes += chunk.byteLength;
|
|
652
661
|
}
|
|
653
662
|
};
|
|
@@ -749,12 +758,12 @@ export class PrologProcess {
|
|
|
749
758
|
return;
|
|
750
759
|
const remaining = this.maxOutputBytes - this.outputBufferBytes;
|
|
751
760
|
if (chunk.byteLength > remaining) {
|
|
752
|
-
this.outputBuffer += chunk.subarray(0, remaining)
|
|
761
|
+
this.outputBuffer += this.decodeOutput(chunk.subarray(0, remaining));
|
|
753
762
|
this.outputBufferBytes = this.maxOutputBytes;
|
|
754
763
|
this.outputOverflowed = true;
|
|
755
764
|
return;
|
|
756
765
|
}
|
|
757
|
-
this.outputBuffer +=
|
|
766
|
+
this.outputBuffer += this.decodeOutput(chunk);
|
|
758
767
|
this.outputBufferBytes += chunk.byteLength;
|
|
759
768
|
this.wakeOutputWaiter();
|
|
760
769
|
}
|
|
@@ -763,12 +772,12 @@ export class PrologProcess {
|
|
|
763
772
|
return;
|
|
764
773
|
const remaining = this.maxOutputBytes - this.errorBufferBytes;
|
|
765
774
|
if (chunk.byteLength > remaining) {
|
|
766
|
-
this.errorBuffer += chunk.subarray(0, remaining)
|
|
775
|
+
this.errorBuffer += this.decodeError(chunk.subarray(0, remaining));
|
|
767
776
|
this.errorBufferBytes = this.maxOutputBytes;
|
|
768
777
|
this.outputOverflowed = true;
|
|
769
778
|
return;
|
|
770
779
|
}
|
|
771
|
-
this.errorBuffer +=
|
|
780
|
+
this.errorBuffer += this.decodeError(chunk);
|
|
772
781
|
this.errorBufferBytes += chunk.byteLength;
|
|
773
782
|
this.wakeOutputWaiter();
|
|
774
783
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../../src/public/impact/analyzer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../../src/public/impact/analyzer.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EACV,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAWpB,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CA0LlC"}
|
|
@@ -7,11 +7,19 @@ import { extractSymbolsFromStagedFileAsync } from "../../traceability/symbol-ext
|
|
|
7
7
|
import { isTraceabilityRelationshipType } from "../symbol-granularity.js";
|
|
8
8
|
import { createSemanticReviewDiagnostics, createSymbolGranularityDiagnostics, } from "./diagnostics.js";
|
|
9
9
|
import { createImpactManifestLookup, readImpactManifestResults, } from "./manifest.js";
|
|
10
|
+
import { createRequirementStatementDiagnostics, readWorkspaceRequirement, workspaceFileChanged, } from "./requirement-statements.js";
|
|
10
11
|
import { normalizeSourceFile } from "./source-changes.js";
|
|
11
12
|
import { collectSourceChanges, uniqueSorted } from "./source-changes.js";
|
|
12
13
|
import { buildNextActions, collectLinkedEntities, formatExtractedSymbols, } from "./summaries.js";
|
|
13
14
|
import { createSymbolQualityDiagnostics } from "./symbol-quality.js";
|
|
14
15
|
import { createUnownedFileDiagnostics } from "./unowned-files.js";
|
|
16
|
+
/** The workspace-relative document a requirement was read from. */
|
|
17
|
+
function requirementSourcePath(requirement) {
|
|
18
|
+
const source = requirement.source;
|
|
19
|
+
return source && !source.includes("://")
|
|
20
|
+
? source.replace(/^\.\//, "")
|
|
21
|
+
: `.kb/requirements/${requirement.id}.md`;
|
|
22
|
+
}
|
|
15
23
|
export async function analyzeChangedFileImpact(options) {
|
|
16
24
|
const workspaceRoot = path.resolve(options.workspaceRoot);
|
|
17
25
|
const snapshot = options.staged
|
|
@@ -47,8 +55,11 @@ export async function analyzeChangedFileImpact(options) {
|
|
|
47
55
|
(filter.size === 0 || filter.has(file.path)))
|
|
48
56
|
.map((file) => file.path)
|
|
49
57
|
: sourceChanges.map((change) => change.file));
|
|
50
|
-
const
|
|
51
|
-
? readSnapshotKnowledge(snapshot.readGit, snapshot.headTree, snapshot.readBlobs)
|
|
58
|
+
const snapshotKnowledge = snapshot
|
|
59
|
+
? readSnapshotKnowledge(snapshot.readGit, snapshot.headTree, snapshot.readBlobs)
|
|
60
|
+
: undefined;
|
|
61
|
+
const manifestResults = snapshotKnowledge
|
|
62
|
+
? snapshotKnowledge.filter((result) => result.entity.type === "symbol")
|
|
52
63
|
: readImpactManifestResults(workspaceRoot);
|
|
53
64
|
const manifestLookup = createImpactManifestLookup(manifestResults);
|
|
54
65
|
const symbolsByFile = new Map();
|
|
@@ -92,6 +103,19 @@ export async function analyzeChangedFileImpact(options) {
|
|
|
92
103
|
requestedFiles: [...filter],
|
|
93
104
|
linkedSourceFiles,
|
|
94
105
|
}),
|
|
106
|
+
// What the changed code is meant to do, quoted, before the
|
|
107
|
+
// structural findings: "no violations" never compared it.
|
|
108
|
+
...createRequirementStatementDiagnostics({
|
|
109
|
+
workspaceRoot,
|
|
110
|
+
symbolsByFile,
|
|
111
|
+
readRequirement: (id) => snapshotKnowledge
|
|
112
|
+
? snapshotKnowledge.find((result) => result.entity.id === id)
|
|
113
|
+
?.entity
|
|
114
|
+
: readWorkspaceRequirement(workspaceRoot, id),
|
|
115
|
+
requirementChanged: (requirement) => snapshot
|
|
116
|
+
? snapshot.inventory.some((file) => file.path === requirementSourcePath(requirement))
|
|
117
|
+
: workspaceFileChanged(workspaceRoot, requirementSourcePath(requirement)),
|
|
118
|
+
}),
|
|
95
119
|
...createSymbolGranularityDiagnostics({
|
|
96
120
|
manifestResults: activeManifestResults,
|
|
97
121
|
symbolsByFile,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ExtractedEntity } from "../../extractors/markdown.js";
|
|
2
|
+
import type { KibiImpactDiagnostic } from "../../traceability/staged-diagnostics.js";
|
|
3
|
+
import type { ExtractedSymbol } from "../../traceability/symbol-extract.js";
|
|
4
|
+
export type RequirementStatementDiagnosticsOptions = {
|
|
5
|
+
readonly workspaceRoot: string;
|
|
6
|
+
readonly symbolsByFile: ReadonlyMap<string, readonly ExtractedSymbol[]>;
|
|
7
|
+
/** The requirement entity for an id, or undefined when it is unknown. */
|
|
8
|
+
readonly readRequirement: (id: string) => ExtractedEntity | undefined;
|
|
9
|
+
/** Whether the requirement's document changed in this change. */
|
|
10
|
+
readonly requirementChanged: (requirement: ExtractedEntity) => boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* One `requirement_statement_unverified` warning per current requirement that
|
|
14
|
+
* a changed behavioral symbol implements, quoting the requirement's statement
|
|
15
|
+
* and firmness. A requirement whose own document changed in the same change
|
|
16
|
+
* is left to the firmness and supersede checks.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createRequirementStatementDiagnostics(options: RequirementStatementDiagnosticsOptions): KibiImpactDiagnostic[];
|
|
19
|
+
/**
|
|
20
|
+
* Whether Git reports any change (staged, unstaged or untracked) to a
|
|
21
|
+
* workspace file against HEAD. Outside a repository nothing counts as changed.
|
|
22
|
+
*/
|
|
23
|
+
export declare function workspaceFileChanged(workspaceRoot: string, relativePath: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Read a requirement from its canonical document in the working tree
|
|
26
|
+
* (`.kb/requirements/<id>.md`).
|
|
27
|
+
*/
|
|
28
|
+
export declare function readWorkspaceRequirement(workspaceRoot: string, id: string): ExtractedEntity | undefined;
|
|
29
|
+
//# sourceMappingURL=requirement-statements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requirement-statements.d.ts","sourceRoot":"","sources":["../../../src/public/impact/requirement-statements.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAgB5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC,CAAC;IACxE,yEAAyE;IACzE,QAAQ,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,eAAe,GAAG,SAAS,CAAC;IACtE,iEAAiE;IACjE,QAAQ,CAAC,kBAAkB,EAAE,CAAC,WAAW,EAAE,eAAe,KAAK,OAAO,CAAC;CACxE,CAAC;AAiBF;;;;;GAKG;AACH,wBAAgB,qCAAqC,CACnD,OAAO,EAAE,sCAAsC,GAC9C,oBAAoB,EAAE,CAsExB;AAGD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GACnB,OAAO,CAeT;AAGD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,EACrB,EAAE,EAAE,MAAM,GACT,eAAe,GAAG,SAAS,CAS7B"}
|