@unified-product-graph/sdk 0.6.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/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/chunk-ARDXTSGG.js +20904 -0
- package/dist/chunk-ARDXTSGG.js.map +1 -0
- package/dist/index.d.ts +678 -0
- package/dist/index.js +2732 -0
- package/dist/index.js.map +1 -0
- package/dist/logic-DriXyFKi.d.ts +1480 -0
- package/dist/logic.d.ts +2 -0
- package/dist/logic.js +95 -0
- package/dist/logic.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { U as UPGFileStore, I as IntegrityReport, a as UPGPortfolioStore } from './logic-DriXyFKi.js';
|
|
2
|
+
export { A as AdjacentEdge, b as AlternateAnchor, c as AnchorHint, B as BUSINESS_AREA_META, d as BusinessArea, C as ChangeEntry, e as CrossEdgeMigrationResult, D as DESCRIPTION_SOFT_LIMIT, f as DanglingEdgeClass, g as DanglingEdgeRecord, h as DanglingEdgeReport, E as ENTITY_CLASSIFICATION, i as EdgeAlias, j as EdgeInferenceFail, k as EdgeInferenceOk, l as EdgeInferenceResult, m as EdgeInferenceSuggestion, n as EdgePairValidationFail, o as EdgePairValidationOk, p as EdgePairValidationResult, q as EvalResult, r as InferEdgeTypeError, s as InspectResult, t as InspectViolation, L as LengthCheckResult, M as MergeResult, u as MissingEntityRow, P as PROPERTY_TREE_SOFT_BYTES, v as PROPERTY_TREE_SOFT_DEPTH, w as PlanResult, x as PortfolioLoadResult, y as PrioritiseExecutionResult, z as PrioritiseFallbackResult, F as PrioritiseRankedRow, G as PropertyTypeCheckResult, H as PropertyTypeViolation, Q as QuarantinedEntity, R as ReflectPrompt, J as ReflectPromptKind, K as ReflectResult, S as SchemaDriftSummary, T as TIER_CLUSTER_META, N as TIER_DESCRIPTIONS, O as TITLE_SOFT_LIMIT, V as Tier, W as TierCluster, X as TraceResult, Y as TraceTrailRow, Z as ValidateGraphBody, _ as buildAdjacentEdges, $ as buildAlternateAnchors, a0 as buildAnchorHint, a1 as buildResolverHints, a2 as checkLengthCaps, a3 as checkPropertyTypes, a4 as classifyDanglingEdges, a5 as collectAntiPatternInputs, a6 as computeSchemaDriftSummary, a7 as edgeId, a8 as evaluateExpression, a9 as executeInspect, aa as executePlan, ab as executePrioritise, ac as executeReflect, ad as executeTrace, ae as getClassification, af as getEntitiesForBusinessArea, ag as getEntitiesForCluster, ah as getEntitiesForTier, ai as getEntitiesForTierAndArea, aj as getEntitiesUpToTier, ak as getTierEntityCount, al as getTierForStage, am as inferEdgeType, an as inferEdgeTypeWithTier, ao as isRelevantForStage, ap as nodeId, aq as productId, ar as renderDanglingReport, as as renderDriftSummary, at as renderPropertyTypeWarning, au as validateClassificationCoverage, av as validateEdgeTypePair } from './logic-DriXyFKi.js';
|
|
3
|
+
import { UPGEdge, UPGProductStage, UPGBaseNode, UPGEdgeType, UPGPortfolio, UPGProductArea, UPGOrganization, UPGPortfolioDocument } from '@unified-product-graph/core';
|
|
4
|
+
export { EntityTypeResolution, UPGCrossEdge, UPGPortfolioDocument, UPG_ANTI_PATTERNS, UPG_REGIONS, UnknownEntityTypeError, resolveEntityType } from '@unified-product-graph/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared tool logic. Pure functions that operate on a UPGFileStore.
|
|
8
|
+
*
|
|
9
|
+
* Used by both the MCP server (server.ts) and the CLI (@unified-product-graph/mcp).
|
|
10
|
+
* Extract here, import everywhere.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Validate a status value against the lifecycle phases for an entity type.
|
|
15
|
+
* Returns a warning string if the status is invalid, or undefined if valid / no lifecycle.
|
|
16
|
+
*/
|
|
17
|
+
declare function validateStatusAgainstLifecycle(entityType: string, status: string): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the initial_phase for an entity type, or undefined if the type has no lifecycle.
|
|
20
|
+
*/
|
|
21
|
+
declare function getDefaultStatus(entityType: string): string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Backfill `slug` on a freshly-built node before it's added to the store.
|
|
24
|
+
* Picks the auto-generated slug from `title`, resolved against
|
|
25
|
+
* every existing slug + alias of the same `type` in the same product.
|
|
26
|
+
*
|
|
27
|
+
* No-op if the node already carries an explicit slug.
|
|
28
|
+
*/
|
|
29
|
+
declare function autoFillSlug(node: UPGBaseNode, store: UPGFileStore): void;
|
|
30
|
+
declare function normalizeTags(tags: unknown): string[] | undefined;
|
|
31
|
+
declare const BUSINESS_AREAS: Record<string, {
|
|
32
|
+
emoji: string;
|
|
33
|
+
types: string[];
|
|
34
|
+
}>;
|
|
35
|
+
declare const STAGE_COVERAGE_TARGETS: Record<UPGProductStage, string[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the canonical UPGProductStage for digest coverage. Returns
|
|
38
|
+
* `'concept'` as the default when stage is missing or unrecognised — concept
|
|
39
|
+
* is the narrowest expectation surface, so we under-grade rather than
|
|
40
|
+
* over-grade. Legacy `"idea"` and friends route through `coerceProductStage`
|
|
41
|
+
* (which lives in `@unified-product-graph/core`); the inline `"idea" →
|
|
42
|
+
* "concept"` fallback below is defensive in case the core helper is ever
|
|
43
|
+
* unavailable.
|
|
44
|
+
*/
|
|
45
|
+
declare function resolveCoverageStage(rawStage: unknown): UPGProductStage;
|
|
46
|
+
declare const LIFECYCLE_PHASES: Record<string, string[]>;
|
|
47
|
+
declare const CHAINS: readonly [{
|
|
48
|
+
readonly name: "persona → job";
|
|
49
|
+
readonly from: "persona";
|
|
50
|
+
readonly to: "job";
|
|
51
|
+
readonly edgePattern: "job";
|
|
52
|
+
}, {
|
|
53
|
+
readonly name: "job → need";
|
|
54
|
+
readonly from: "job";
|
|
55
|
+
readonly to: "need";
|
|
56
|
+
readonly edgePattern: "need";
|
|
57
|
+
}, {
|
|
58
|
+
readonly name: "opportunity → solution";
|
|
59
|
+
readonly from: "opportunity";
|
|
60
|
+
readonly to: "solution";
|
|
61
|
+
readonly edgePattern: "solution";
|
|
62
|
+
}, {
|
|
63
|
+
readonly name: "solution → hypothesis";
|
|
64
|
+
readonly from: "solution";
|
|
65
|
+
readonly to: "hypothesis";
|
|
66
|
+
readonly edgePattern: "hypothesis";
|
|
67
|
+
}, {
|
|
68
|
+
readonly name: "hypothesis → experiment_plan";
|
|
69
|
+
readonly from: "hypothesis";
|
|
70
|
+
readonly to: "experiment_plan";
|
|
71
|
+
readonly edgePattern: "experiment_plan";
|
|
72
|
+
}, {
|
|
73
|
+
readonly name: "experiment_run → learning";
|
|
74
|
+
readonly from: "experiment_run";
|
|
75
|
+
readonly to: "learning";
|
|
76
|
+
readonly edgePattern: "learning";
|
|
77
|
+
}, {
|
|
78
|
+
readonly name: "objective → key_result";
|
|
79
|
+
readonly from: "objective";
|
|
80
|
+
readonly to: "key_result";
|
|
81
|
+
readonly edgePattern: "key_result";
|
|
82
|
+
}, {
|
|
83
|
+
readonly name: "feature → story_statement";
|
|
84
|
+
readonly from: "feature";
|
|
85
|
+
readonly to: "story_statement";
|
|
86
|
+
readonly edgePattern: "story_statement";
|
|
87
|
+
}];
|
|
88
|
+
/** Get sort priority for a type (lower = higher in tree). Unknown types sort last. */
|
|
89
|
+
declare function typeSortPriority(type: string): number;
|
|
90
|
+
/** Sort nodes by type priority, then alphabetically by title within same type */
|
|
91
|
+
declare function sortByType(nodes: UPGBaseNode[]): UPGBaseNode[];
|
|
92
|
+
interface CoverageRegion {
|
|
93
|
+
covered: number;
|
|
94
|
+
total: number;
|
|
95
|
+
/**
|
|
96
|
+
* NEW (Finding 9 / UPG-512): True when this region is on the product
|
|
97
|
+
* stage's expected-coverage list. Regions where this is `false` are
|
|
98
|
+
* surfaced for awareness but excluded from `stage_summary.overall_pct`.
|
|
99
|
+
*/
|
|
100
|
+
counted_toward_stage: boolean;
|
|
101
|
+
types_present: string[];
|
|
102
|
+
types_missing: string[];
|
|
103
|
+
}
|
|
104
|
+
interface CoverageStageSummary {
|
|
105
|
+
stage: UPGProductStage;
|
|
106
|
+
/** Number of regions counted toward this stage's completeness. */
|
|
107
|
+
regions_counted: number;
|
|
108
|
+
/** Counted regions that are fully covered (covered === total). */
|
|
109
|
+
regions_complete: number;
|
|
110
|
+
/** Counted regions with partial coverage (0 < covered < total). */
|
|
111
|
+
regions_partial: number;
|
|
112
|
+
/** Whole-number percentage 0-100, averaged across counted regions only. */
|
|
113
|
+
overall_pct: number;
|
|
114
|
+
}
|
|
115
|
+
interface GraphDigest {
|
|
116
|
+
product: {
|
|
117
|
+
title: string;
|
|
118
|
+
stage: string;
|
|
119
|
+
};
|
|
120
|
+
counts: {
|
|
121
|
+
total_nodes: number;
|
|
122
|
+
total_edges: number;
|
|
123
|
+
by_type: Record<string, number>;
|
|
124
|
+
};
|
|
125
|
+
health: {
|
|
126
|
+
orphan_count: number;
|
|
127
|
+
orphan_rate: number;
|
|
128
|
+
connectivity: number;
|
|
129
|
+
validation_rate: number;
|
|
130
|
+
user_coverage: number;
|
|
131
|
+
};
|
|
132
|
+
chains: Record<string, number>;
|
|
133
|
+
/**
|
|
134
|
+
* Per-region coverage map. Keys are `BUSINESS_AREAS` ids (e.g.
|
|
135
|
+
* `identity`, `understanding`). The new stage-aware aggregate is at
|
|
136
|
+
* `coverage.stage_summary` (typed loosely here so `Record<string,
|
|
137
|
+
* CoverageRegion>` index lookups stay correct for callers).
|
|
138
|
+
*
|
|
139
|
+
* NOTE: callers iterating `Object.entries(coverage)` should skip the
|
|
140
|
+
* `stage_summary` key — it carries a different shape.
|
|
141
|
+
*/
|
|
142
|
+
coverage: Record<string, CoverageRegion> & {
|
|
143
|
+
stage_summary?: CoverageStageSummary;
|
|
144
|
+
};
|
|
145
|
+
lifecycle: Record<string, number>;
|
|
146
|
+
}
|
|
147
|
+
declare function computeGraphDigest(store: UPGFileStore): GraphDigest;
|
|
148
|
+
interface SearchResult {
|
|
149
|
+
node: UPGBaseNode;
|
|
150
|
+
score: number;
|
|
151
|
+
match_field: string;
|
|
152
|
+
}
|
|
153
|
+
declare function searchNodes(store: UPGFileStore, query: string, options?: {
|
|
154
|
+
type?: string;
|
|
155
|
+
fields?: string[];
|
|
156
|
+
limit?: number;
|
|
157
|
+
}): SearchResult[];
|
|
158
|
+
declare function computeHealthScore(digest: GraphDigest): number;
|
|
159
|
+
declare function getOrphans(store: UPGFileStore): UPGBaseNode[];
|
|
160
|
+
interface ListNodesOptions {
|
|
161
|
+
type?: string;
|
|
162
|
+
status?: string;
|
|
163
|
+
parentId?: string;
|
|
164
|
+
tags?: string[];
|
|
165
|
+
includeEdges?: boolean;
|
|
166
|
+
countOnly?: boolean;
|
|
167
|
+
limit?: number;
|
|
168
|
+
offset?: number;
|
|
169
|
+
}
|
|
170
|
+
interface ListNodesResult {
|
|
171
|
+
nodes: Array<Record<string, unknown>>;
|
|
172
|
+
total: number;
|
|
173
|
+
}
|
|
174
|
+
declare function listNodes(store: UPGFileStore, options?: ListNodesOptions): ListNodesResult;
|
|
175
|
+
interface GetNodeResult {
|
|
176
|
+
node: UPGBaseNode;
|
|
177
|
+
edges_out: Array<Record<string, unknown>>;
|
|
178
|
+
edges_in: Array<Record<string, unknown>>;
|
|
179
|
+
}
|
|
180
|
+
declare function getNode(store: UPGFileStore, args: {
|
|
181
|
+
node_id: string;
|
|
182
|
+
compact_edges?: boolean;
|
|
183
|
+
}): GetNodeResult | null;
|
|
184
|
+
interface GetNodesResult {
|
|
185
|
+
nodes: Array<GetNodeResult>;
|
|
186
|
+
total: number;
|
|
187
|
+
not_found?: string[];
|
|
188
|
+
}
|
|
189
|
+
declare function getNodes(store: UPGFileStore, args: {
|
|
190
|
+
ids: string[];
|
|
191
|
+
compact_edges?: boolean;
|
|
192
|
+
}): GetNodesResult;
|
|
193
|
+
interface CreateNodeArgs {
|
|
194
|
+
type: string;
|
|
195
|
+
title: string;
|
|
196
|
+
description?: string;
|
|
197
|
+
tags?: unknown;
|
|
198
|
+
status?: string;
|
|
199
|
+
properties?: Record<string, unknown>;
|
|
200
|
+
parent_id?: string;
|
|
201
|
+
}
|
|
202
|
+
interface CreateNodeResult {
|
|
203
|
+
node: UPGBaseNode;
|
|
204
|
+
edge: UPGEdge | null;
|
|
205
|
+
warning?: string;
|
|
206
|
+
}
|
|
207
|
+
declare function createNode(store: UPGFileStore, args: CreateNodeArgs): CreateNodeResult;
|
|
208
|
+
interface CreateEdgeArgs {
|
|
209
|
+
source_id: string;
|
|
210
|
+
target_id?: string;
|
|
211
|
+
target_title?: string;
|
|
212
|
+
target_type?: string;
|
|
213
|
+
type?: string;
|
|
214
|
+
}
|
|
215
|
+
type CreateEdgeResult = {
|
|
216
|
+
edge: UPGEdge;
|
|
217
|
+
warning?: string;
|
|
218
|
+
} | {
|
|
219
|
+
error: string;
|
|
220
|
+
/**
|
|
221
|
+
* Source/target types when the failure is a "no canonical edge"
|
|
222
|
+
* resolver miss — surfaced so the MCP handler can attach
|
|
223
|
+
* `anchor_hint` / `alternate_anchors` / `adjacent_edges` enrichment
|
|
224
|
+
* blocks (UPG-505 + UPG-515).
|
|
225
|
+
*/
|
|
226
|
+
no_canonical_edge_for?: {
|
|
227
|
+
source_type: string;
|
|
228
|
+
target_type: string;
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
declare function createEdge(store: UPGFileStore, args: CreateEdgeArgs): CreateEdgeResult;
|
|
232
|
+
interface DeleteNodeResult {
|
|
233
|
+
deleted_node_id: string;
|
|
234
|
+
deleted_node_title: string;
|
|
235
|
+
deleted_edge_ids: string[];
|
|
236
|
+
}
|
|
237
|
+
declare function deleteNode(store: UPGFileStore, args: {
|
|
238
|
+
node_id: string;
|
|
239
|
+
}): DeleteNodeResult;
|
|
240
|
+
interface DeleteEdgeResult {
|
|
241
|
+
deleted_edge_id: string;
|
|
242
|
+
}
|
|
243
|
+
declare function deleteEdge(store: UPGFileStore, args: {
|
|
244
|
+
edge_id: string;
|
|
245
|
+
}): DeleteEdgeResult;
|
|
246
|
+
interface MoveNodeArgs {
|
|
247
|
+
node_id: string;
|
|
248
|
+
new_parent_id: string;
|
|
249
|
+
/** Override the inferred edge type. Must be a key in UPG_EDGE_CATALOG. */
|
|
250
|
+
new_edge_type?: string;
|
|
251
|
+
/**
|
|
252
|
+
* Disambiguate when the node has more than one hierarchy edge. Caller
|
|
253
|
+
* specifies which existing parent edge to delete; otherwise the move is
|
|
254
|
+
* rejected with the candidate edge ids.
|
|
255
|
+
*/
|
|
256
|
+
old_edge_id?: string;
|
|
257
|
+
}
|
|
258
|
+
type MoveNodeResult = {
|
|
259
|
+
moved: true;
|
|
260
|
+
node_id: string;
|
|
261
|
+
new_edge: UPGEdge;
|
|
262
|
+
removed_edge_id: string | null;
|
|
263
|
+
/** Removed edge object — exposed for caller-driven rollback (e.g. batch). */
|
|
264
|
+
removed_edge?: UPGEdge;
|
|
265
|
+
warning?: string;
|
|
266
|
+
} | {
|
|
267
|
+
moved: false;
|
|
268
|
+
error: string;
|
|
269
|
+
};
|
|
270
|
+
declare function moveNode(store: UPGFileStore, args: MoveNodeArgs): MoveNodeResult;
|
|
271
|
+
interface BatchMoveNodesResult {
|
|
272
|
+
moves: Array<{
|
|
273
|
+
node_id: string;
|
|
274
|
+
new_edge: UPGEdge;
|
|
275
|
+
removed_edge_id: string | null;
|
|
276
|
+
}>;
|
|
277
|
+
count: number;
|
|
278
|
+
warnings?: string[];
|
|
279
|
+
}
|
|
280
|
+
type BatchMoveNodesOutcome = {
|
|
281
|
+
ok: true;
|
|
282
|
+
result: BatchMoveNodesResult;
|
|
283
|
+
} | {
|
|
284
|
+
ok: false;
|
|
285
|
+
error: string;
|
|
286
|
+
failed_at_index: number | null;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Apply a batch of moves atomically. Validates every move against the catalog
|
|
290
|
+
* BEFORE any mutation; on the first failure the batch is rejected with no
|
|
291
|
+
* changes to the graph. If a mutation fails mid-application (highly unusual
|
|
292
|
+
* given upfront validation), already-applied moves are rolled back.
|
|
293
|
+
*/
|
|
294
|
+
declare function batchMoveNodes(store: UPGFileStore, moves: MoveNodeArgs[]): BatchMoveNodesOutcome;
|
|
295
|
+
interface BatchNodeInput {
|
|
296
|
+
type: string;
|
|
297
|
+
title: string;
|
|
298
|
+
description?: string;
|
|
299
|
+
status?: string;
|
|
300
|
+
tags?: unknown;
|
|
301
|
+
properties?: Record<string, unknown>;
|
|
302
|
+
parent_id?: string;
|
|
303
|
+
parent_ref?: string;
|
|
304
|
+
}
|
|
305
|
+
interface BatchEdgeInput {
|
|
306
|
+
from_ref: string;
|
|
307
|
+
to_ref: string;
|
|
308
|
+
type?: string;
|
|
309
|
+
}
|
|
310
|
+
interface BatchCreateArgs {
|
|
311
|
+
nodes: BatchNodeInput[];
|
|
312
|
+
edges?: BatchEdgeInput[];
|
|
313
|
+
}
|
|
314
|
+
interface BatchCreateOk {
|
|
315
|
+
ok: true;
|
|
316
|
+
created: Array<{
|
|
317
|
+
id: string;
|
|
318
|
+
type: string;
|
|
319
|
+
title: string;
|
|
320
|
+
status?: string;
|
|
321
|
+
}>;
|
|
322
|
+
edges: UPGEdge[];
|
|
323
|
+
explicit_edges?: UPGEdge[];
|
|
324
|
+
count: number;
|
|
325
|
+
warnings?: string[];
|
|
326
|
+
}
|
|
327
|
+
interface BatchCreateFail {
|
|
328
|
+
ok: false;
|
|
329
|
+
error: string;
|
|
330
|
+
}
|
|
331
|
+
type BatchCreateResult = BatchCreateOk | BatchCreateFail;
|
|
332
|
+
/**
|
|
333
|
+
* Atomic batch creation — nodes plus optional explicit edges in a single
|
|
334
|
+
* all-or-nothing transaction.
|
|
335
|
+
*
|
|
336
|
+
* Validation pass walks every node and every edge against the canonical
|
|
337
|
+
* schema BEFORE any mutation. If anything fails, no nodes and no edges land.
|
|
338
|
+
*
|
|
339
|
+
* Apply pass creates nodes (with parent_ref / parent_id auto-edges from
|
|
340
|
+
* inference; failed inference = warning, never fabrication),
|
|
341
|
+
* then creates the explicit edges. If ANY apply step throws, rolls back
|
|
342
|
+
* every already-applied node + edge so the graph is bit-for-bit identical
|
|
343
|
+
* to the pre-call state.
|
|
344
|
+
*/
|
|
345
|
+
declare function batchCreateNodes(store: UPGFileStore, args: BatchCreateArgs): BatchCreateResult;
|
|
346
|
+
interface MigrateNodeTypeArgs {
|
|
347
|
+
node_id: string;
|
|
348
|
+
new_type: string;
|
|
349
|
+
}
|
|
350
|
+
type MigrateNodeTypeResult = {
|
|
351
|
+
migrated: true;
|
|
352
|
+
node_id: string;
|
|
353
|
+
from_type: string;
|
|
354
|
+
to_type: string;
|
|
355
|
+
edges_rewritten: Array<{
|
|
356
|
+
id: string;
|
|
357
|
+
from: string;
|
|
358
|
+
to: string;
|
|
359
|
+
}>;
|
|
360
|
+
warning?: string;
|
|
361
|
+
} | {
|
|
362
|
+
migrated: false;
|
|
363
|
+
error: string;
|
|
364
|
+
suggestions?: string[];
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* Atomically change a single node's entity type and rewrite every incident
|
|
368
|
+
* edge to its new canonical edge type derived from the catalog.
|
|
369
|
+
*
|
|
370
|
+
* Distinct from `migrate_type` (which rewrites EVERY node of a given type
|
|
371
|
+
* across the whole graph by string substitution). This single-node variant
|
|
372
|
+
* re-infers each affected edge from (source.type, target.type) via
|
|
373
|
+
* `inferEdgeTypeWithTier`, so it preserves correctness when the graph mixes
|
|
374
|
+
* canonical and deprecated types.
|
|
375
|
+
*
|
|
376
|
+
* If any incident edge cannot be re-inferred, the entire migration is
|
|
377
|
+
* rejected and the graph is left unchanged. Rollback restores both the
|
|
378
|
+
* node's original type and any edges already rewritten.
|
|
379
|
+
*/
|
|
380
|
+
declare function migrateNodeType(store: UPGFileStore, args: MigrateNodeTypeArgs): MigrateNodeTypeResult;
|
|
381
|
+
|
|
382
|
+
interface UPGClientOptions {
|
|
383
|
+
/** Path to the .upg file on disk. */
|
|
384
|
+
file: string;
|
|
385
|
+
/**
|
|
386
|
+
* Skip auto-load on first operation (default: false).
|
|
387
|
+
* When true, call `await client.load()` manually before any operation.
|
|
388
|
+
*/
|
|
389
|
+
lazy?: boolean;
|
|
390
|
+
}
|
|
391
|
+
interface NodeListOptions extends ListNodesOptions {
|
|
392
|
+
}
|
|
393
|
+
interface EdgeListOptions {
|
|
394
|
+
source?: string;
|
|
395
|
+
target?: string;
|
|
396
|
+
type?: UPGEdgeType;
|
|
397
|
+
}
|
|
398
|
+
interface HealthResult {
|
|
399
|
+
score: number;
|
|
400
|
+
digest: GraphDigest;
|
|
401
|
+
}
|
|
402
|
+
interface SearchOptions {
|
|
403
|
+
/** Maximum number of results (default: 20). */
|
|
404
|
+
limit?: number;
|
|
405
|
+
/** Restrict to a single entity type. */
|
|
406
|
+
type?: string;
|
|
407
|
+
}
|
|
408
|
+
declare class UPGClient {
|
|
409
|
+
private readonly options;
|
|
410
|
+
private store;
|
|
411
|
+
private loadPromise;
|
|
412
|
+
/** Node operations namespace. */
|
|
413
|
+
readonly nodes: NodesAPI;
|
|
414
|
+
/** Edge operations namespace. */
|
|
415
|
+
readonly edges: EdgesAPI;
|
|
416
|
+
constructor(options: UPGClientOptions);
|
|
417
|
+
/**
|
|
418
|
+
* Load the .upg file. Called automatically on first operation unless
|
|
419
|
+
* `{ lazy: true }` was set. Safe to call multiple times — repeated calls
|
|
420
|
+
* are coalesced. If load fails (transient I/O, parse error, etc.) the
|
|
421
|
+
* promise is rejected AND the cached promise is cleared, so the next
|
|
422
|
+
* call retries from scratch rather than re-throwing the stale error.
|
|
423
|
+
*/
|
|
424
|
+
load(): Promise<void>;
|
|
425
|
+
/** Internal: get the loaded store, loading on demand. */
|
|
426
|
+
getStore(): Promise<UPGFileStore>;
|
|
427
|
+
/** Persist pending changes to disk. Called automatically after mutations. */
|
|
428
|
+
flush(): Promise<void>;
|
|
429
|
+
/** Compute a health score (0–100) plus the underlying graph digest. */
|
|
430
|
+
health(): Promise<HealthResult>;
|
|
431
|
+
/** Search nodes by free-text query. */
|
|
432
|
+
search(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
433
|
+
/**
|
|
434
|
+
* Verify integrity of the loaded graph. Returns the integrity report from
|
|
435
|
+
* the last load + any in-memory mutation checks. `null` indicates no
|
|
436
|
+
* integrity issues were detected.
|
|
437
|
+
*/
|
|
438
|
+
verify(): Promise<IntegrityReport | null>;
|
|
439
|
+
/**
|
|
440
|
+
* Diff against a previous version. Not yet implemented — tracked in
|
|
441
|
+
* UPG-541 follow-up. Will return a structured changeset between the
|
|
442
|
+
* current graph and the named ref (git revision or snapshot id).
|
|
443
|
+
*/
|
|
444
|
+
diff(_ref: string): Promise<never>;
|
|
445
|
+
/** Release file watchers and free resources. */
|
|
446
|
+
close(): Promise<void>;
|
|
447
|
+
}
|
|
448
|
+
declare class NodesAPI {
|
|
449
|
+
private readonly client;
|
|
450
|
+
constructor(client: UPGClient);
|
|
451
|
+
/**
|
|
452
|
+
* Create a node. The `type` is validated against the UPG entity catalog —
|
|
453
|
+
* deprecated aliases are accepted with a warning, genuinely unknown types
|
|
454
|
+
* throw `UnknownEntityTypeError`.
|
|
455
|
+
*/
|
|
456
|
+
create(args: CreateNodeArgs): Promise<CreateNodeResult>;
|
|
457
|
+
/** List nodes, optionally filtered by type / status / tag. */
|
|
458
|
+
list(options?: NodeListOptions): Promise<ListNodesResult>;
|
|
459
|
+
/** Get a single node by id. Returns `undefined` if not found. */
|
|
460
|
+
get(id: string): Promise<UPGBaseNode | undefined>;
|
|
461
|
+
/** Update a node by id. Returns the updated node. */
|
|
462
|
+
update(id: string, patch: Partial<UPGBaseNode>): Promise<UPGBaseNode>;
|
|
463
|
+
/** Delete a node and all incident edges. */
|
|
464
|
+
delete(id: string): Promise<DeleteNodeResult>;
|
|
465
|
+
}
|
|
466
|
+
declare class EdgesAPI {
|
|
467
|
+
private readonly client;
|
|
468
|
+
constructor(client: UPGClient);
|
|
469
|
+
/**
|
|
470
|
+
* Connect two nodes with an edge. Edge type is inferred from source +
|
|
471
|
+
* target entity types if not provided.
|
|
472
|
+
*/
|
|
473
|
+
connect(sourceId: string, targetId: string, opts?: Partial<CreateEdgeArgs>): Promise<CreateEdgeResult>;
|
|
474
|
+
/** List edges, optionally filtered by source / target / type. */
|
|
475
|
+
list(options?: EdgeListOptions): Promise<UPGEdge[]>;
|
|
476
|
+
/** Delete an edge by id. */
|
|
477
|
+
delete(id: string): Promise<DeleteEdgeResult>;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Workspace bootstrap. Holds the core logic for `init_workspace`.
|
|
482
|
+
*
|
|
483
|
+
* Extracted from server.ts so the bug-prone fs choreography can be unit-tested
|
|
484
|
+
* against real tmp directories without booting the MCP transport.
|
|
485
|
+
*
|
|
486
|
+
* Two bugs this module guards against:
|
|
487
|
+
* 1. `readdir` returning the `.upg` workspace directory itself as an entry
|
|
488
|
+
* that "ends with .upg". The old code tried to rename `.upg → .upg/.upg`
|
|
489
|
+
* and crashed with EINVAL.
|
|
490
|
+
* 2. A user who already organised their `.upg` files inside `.upg/` re-running
|
|
491
|
+
* `init_workspace` from the project root. The old code found nothing at
|
|
492
|
+
* root and registered an empty product list. The new code discovers
|
|
493
|
+
* pre-existing siblings and registers them non-destructively.
|
|
494
|
+
*/
|
|
495
|
+
|
|
496
|
+
interface WorkspaceProduct {
|
|
497
|
+
file: string;
|
|
498
|
+
title: string;
|
|
499
|
+
}
|
|
500
|
+
interface InitWorkspaceArgs {
|
|
501
|
+
cwd: string;
|
|
502
|
+
store: UPGFileStore;
|
|
503
|
+
moveExisting?: boolean;
|
|
504
|
+
}
|
|
505
|
+
interface InitWorkspaceResult {
|
|
506
|
+
workspace_path: string;
|
|
507
|
+
default_product: string;
|
|
508
|
+
products: WorkspaceProduct[];
|
|
509
|
+
current_product: {
|
|
510
|
+
title: string;
|
|
511
|
+
entities: number;
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
declare class WorkspaceAlreadyExistsError extends Error {
|
|
515
|
+
constructor();
|
|
516
|
+
}
|
|
517
|
+
declare class WorkspaceNotInitialisedError extends Error {
|
|
518
|
+
constructor();
|
|
519
|
+
}
|
|
520
|
+
declare class InvalidProductNameError extends Error {
|
|
521
|
+
constructor(reason: string);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Thrown when a `create_product` / product-stage write attempts to
|
|
525
|
+
* persist a non-canonical UPGProductStage value. Surfaces the canonical set
|
|
526
|
+
* + any documented coercion target in the error message so the caller can
|
|
527
|
+
* fix the input.
|
|
528
|
+
*/
|
|
529
|
+
declare class InvalidProductStageError extends Error {
|
|
530
|
+
constructor(message: string);
|
|
531
|
+
}
|
|
532
|
+
declare function initWorkspace({ cwd, store, moveExisting, }: InitWorkspaceArgs): Promise<InitWorkspaceResult>;
|
|
533
|
+
interface CreateProductArgs {
|
|
534
|
+
cwd: string;
|
|
535
|
+
store: UPGFileStore;
|
|
536
|
+
name: string;
|
|
537
|
+
slug?: string;
|
|
538
|
+
description?: string;
|
|
539
|
+
stage?: UPGProductStage;
|
|
540
|
+
/**
|
|
541
|
+
* Optional portfolio node id in the CURRENT loaded store. When provided,
|
|
542
|
+
* a `product` node + `portfolio_contains_product` edge are created in the
|
|
543
|
+
* current store to express the hierarchy (portfolios live in a single .upg).
|
|
544
|
+
*/
|
|
545
|
+
portfolio_id?: string;
|
|
546
|
+
}
|
|
547
|
+
interface CreateProductResult {
|
|
548
|
+
id: string;
|
|
549
|
+
file: string;
|
|
550
|
+
slug: string;
|
|
551
|
+
title: string;
|
|
552
|
+
workspace_path: string;
|
|
553
|
+
portfolio_attached: boolean;
|
|
554
|
+
}
|
|
555
|
+
declare function createProduct(args: CreateProductArgs): Promise<CreateProductResult>;
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Portfolio routing helpers — UPG-526.
|
|
559
|
+
*
|
|
560
|
+
* Portfolio-scoped entity types (`portfolio`, `organization`, `product_area`)
|
|
561
|
+
* belong in `.upg/portfolio.upg` rather than the active product's `nodes[]`.
|
|
562
|
+
* These helpers centralise:
|
|
563
|
+
* - resolving the portfolio path for the current workspace
|
|
564
|
+
* - loading-or-initialising the portfolio store
|
|
565
|
+
* - appending entities to the right portfolio array (or setting the
|
|
566
|
+
* singleton `organization`)
|
|
567
|
+
* - reading portfolio-scoped entities back out in a shape compatible with
|
|
568
|
+
* `list_portfolios` / `list_product_areas`
|
|
569
|
+
*
|
|
570
|
+
* The set of portfolio-scoped types is intentionally narrow and documented as
|
|
571
|
+
* `PORTFOLIO_SCOPED_TYPES`. Adding a new type means:
|
|
572
|
+
* 1. extending `UPGPortfolioDocument` in `@unified-product-graph/core`
|
|
573
|
+
* 2. extending this set
|
|
574
|
+
* 3. extending the write-routing switch in `writePortfolioScopedNode`
|
|
575
|
+
*/
|
|
576
|
+
|
|
577
|
+
/** Entity types that live in `.upg/portfolio.upg` instead of a product graph. */
|
|
578
|
+
declare const PORTFOLIO_SCOPED_TYPES: ReadonlySet<string>;
|
|
579
|
+
/** Default portfolio filename within a `.upg/` workspace. */
|
|
580
|
+
declare const PORTFOLIO_FILENAME = "portfolio.upg";
|
|
581
|
+
/** True when the entity type belongs in the portfolio document. */
|
|
582
|
+
declare function isPortfolioScopedType(type: string): boolean;
|
|
583
|
+
/**
|
|
584
|
+
* Resolve the portfolio file path for the current workspace.
|
|
585
|
+
* Returns null if the cwd has no `.upg/` directory.
|
|
586
|
+
*/
|
|
587
|
+
declare function resolvePortfolioPath(cwd: string): string | null;
|
|
588
|
+
/**
|
|
589
|
+
* Resolve the portfolio file path, creating the `.upg/` directory if it does
|
|
590
|
+
* not exist. Used by write paths that need to mint a portfolio document on
|
|
591
|
+
* demand (e.g. first portfolio entity in a workspace that has product files but
|
|
592
|
+
* never had a portfolio doc).
|
|
593
|
+
*/
|
|
594
|
+
declare function resolveOrCreatePortfolioPath(cwd: string): string;
|
|
595
|
+
/**
|
|
596
|
+
* Open (or initialise) the portfolio store at the given path. Caller is
|
|
597
|
+
* responsible for `flush()`-ing the store when the mutation is committed.
|
|
598
|
+
*/
|
|
599
|
+
declare function openPortfolioStore(portfolioPath: string): Promise<UPGPortfolioStore>;
|
|
600
|
+
interface WritePortfolioNodeArgs {
|
|
601
|
+
/** Entity type — must satisfy `isPortfolioScopedType`. */
|
|
602
|
+
type: string;
|
|
603
|
+
/** Title for the new entity (or the organisation). */
|
|
604
|
+
title: string;
|
|
605
|
+
description?: string;
|
|
606
|
+
/** Free-form properties; subset is hoisted onto the typed shape per type. */
|
|
607
|
+
properties?: Record<string, unknown>;
|
|
608
|
+
/** When type === 'organization', allow overwriting an existing org. */
|
|
609
|
+
overwrite_organization?: boolean;
|
|
610
|
+
}
|
|
611
|
+
interface WritePortfolioNodeResult {
|
|
612
|
+
/** Persisted entity shape (the typed record actually written). */
|
|
613
|
+
entity: UPGPortfolio | UPGProductArea | UPGOrganization;
|
|
614
|
+
/** Where in the portfolio document the entity was written. */
|
|
615
|
+
written_to: 'portfolios' | 'product_areas' | 'organization';
|
|
616
|
+
/** Absolute portfolio file path. */
|
|
617
|
+
portfolio_file: string;
|
|
618
|
+
/** Optional warning (e.g. organization overwrite happened). */
|
|
619
|
+
warning?: string;
|
|
620
|
+
}
|
|
621
|
+
declare class PortfolioRoutingError extends Error {
|
|
622
|
+
constructor(message: string);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Append (or set, for `organization`) a portfolio-scoped entity into the
|
|
626
|
+
* portfolio document. Creates `.upg/portfolio.upg` on demand.
|
|
627
|
+
*
|
|
628
|
+
* For `organization`: refuses to overwrite an existing org unless
|
|
629
|
+
* `overwrite_organization: true` is supplied. The auto-generated org from
|
|
630
|
+
* `loadOrInit` is treated as a placeholder (org id starting with `org_` and
|
|
631
|
+
* title "Portfolio") and may be replaced silently.
|
|
632
|
+
*/
|
|
633
|
+
declare function writePortfolioScopedNode(cwd: string, args: WritePortfolioNodeArgs): Promise<WritePortfolioNodeResult>;
|
|
634
|
+
/**
|
|
635
|
+
* Open the portfolio store if one exists. Returns null when there is no
|
|
636
|
+
* workspace and no portfolio document on disk — callers use this to render an
|
|
637
|
+
* empty list instead of erroring.
|
|
638
|
+
*/
|
|
639
|
+
declare function openPortfolioStoreIfExists(cwd: string): Promise<UPGPortfolioStore | null>;
|
|
640
|
+
/**
|
|
641
|
+
* A lightweight product reference recorded on the portfolio document. The full
|
|
642
|
+
* UPG spec (`UPGPortfolioDocument.products`) types this slot as
|
|
643
|
+
* `UPGProduct & { nodes; edges }`, but the MCP model keeps each product in its
|
|
644
|
+
* own `.upg` file. The reference shape below carries just enough to look the
|
|
645
|
+
* product up later (id + file_path) plus a denormalised title for human-
|
|
646
|
+
* readable listings.
|
|
647
|
+
*
|
|
648
|
+
* If/when the spec adds an explicit `UPGProductReference` type, this shape
|
|
649
|
+
* should migrate to it. The fields are intentionally additive — every field
|
|
650
|
+
* other than `id` is optional so the record stays forward-compatible.
|
|
651
|
+
*/
|
|
652
|
+
interface PortfolioProductReference {
|
|
653
|
+
id: string;
|
|
654
|
+
/** Workspace-relative path to the product's `.upg` file, when known. */
|
|
655
|
+
file_path?: string;
|
|
656
|
+
/** Display title; denormalised from the product's own `product.title`. */
|
|
657
|
+
title?: string;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Ensure that a product is registered on `portfolio.upg.products[]`. No-op when
|
|
661
|
+
* an entry with the same `id` already exists. Does NOT flush — caller flushes
|
|
662
|
+
* once after registering both source and target products in a single pass.
|
|
663
|
+
*
|
|
664
|
+
* @returns true when a new entry was appended, false when already present.
|
|
665
|
+
*/
|
|
666
|
+
declare function registerProductOnPortfolio(doc: UPGPortfolioDocument, ref: PortfolioProductReference): boolean;
|
|
667
|
+
/**
|
|
668
|
+
* Best-effort lookup of a product's `.upg` file and title given its product
|
|
669
|
+
* id. Walks the workspace `.upg/` directory looking for a file whose
|
|
670
|
+
* `product.id` matches. Returns null when not found or when the workspace
|
|
671
|
+
* lookup fails.
|
|
672
|
+
*/
|
|
673
|
+
declare function findProductFileById(cwd: string, productId: string): {
|
|
674
|
+
file_path: string;
|
|
675
|
+
title: string;
|
|
676
|
+
} | null;
|
|
677
|
+
|
|
678
|
+
export { BUSINESS_AREAS, type BatchCreateArgs, type BatchCreateFail, type BatchCreateOk, type BatchCreateResult, type BatchEdgeInput, type BatchMoveNodesOutcome, type BatchMoveNodesResult, type BatchNodeInput, CHAINS, type CoverageRegion, type CoverageStageSummary, type CreateEdgeArgs, type CreateEdgeResult, type CreateNodeArgs, type CreateNodeResult, type CreateProductArgs, type CreateProductResult, type DeleteEdgeResult, type DeleteNodeResult, type EdgeListOptions, type GetNodeResult, type GetNodesResult, type GraphDigest, type HealthResult, type InitWorkspaceArgs, type InitWorkspaceResult, IntegrityReport, InvalidProductNameError, InvalidProductStageError, LIFECYCLE_PHASES, type ListNodesOptions, type ListNodesResult, type MigrateNodeTypeArgs, type MigrateNodeTypeResult, type MoveNodeArgs, type MoveNodeResult, type NodeListOptions, PORTFOLIO_FILENAME, PORTFOLIO_SCOPED_TYPES, type PortfolioProductReference, PortfolioRoutingError, STAGE_COVERAGE_TARGETS, type SearchOptions, type SearchResult, UPGClient, type UPGClientOptions, UPGFileStore, UPGPortfolioStore, WorkspaceAlreadyExistsError, WorkspaceNotInitialisedError, type WorkspaceProduct, type WritePortfolioNodeArgs, type WritePortfolioNodeResult, autoFillSlug, batchCreateNodes, batchMoveNodes, computeGraphDigest, computeHealthScore, createEdge, createNode, createProduct, deleteEdge, deleteNode, findProductFileById, getDefaultStatus, getNode, getNodes, getOrphans, initWorkspace, isPortfolioScopedType, listNodes, migrateNodeType, moveNode, normalizeTags, openPortfolioStore, openPortfolioStoreIfExists, registerProductOnPortfolio, resolveCoverageStage, resolveOrCreatePortfolioPath, resolvePortfolioPath, searchNodes, sortByType, typeSortPriority, validateStatusAgainstLifecycle, writePortfolioScopedNode };
|