trellis 2.0.13 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/cli/index.js +1 -1
  2. package/dist/embeddings/index.js +1 -1
  3. package/dist/{index-7gvjxt27.js → index-2917tjd8.js} +1 -1
  4. package/package.json +2 -10
  5. package/dist/transformers.node-bx3q9d7k.js +0 -33130
  6. package/src/cli/index.ts +0 -3356
  7. package/src/core/agents/harness.ts +0 -380
  8. package/src/core/agents/index.ts +0 -18
  9. package/src/core/agents/types.ts +0 -90
  10. package/src/core/index.ts +0 -118
  11. package/src/core/kernel/middleware.ts +0 -44
  12. package/src/core/kernel/trellis-kernel.ts +0 -593
  13. package/src/core/ontology/builtins.ts +0 -248
  14. package/src/core/ontology/index.ts +0 -34
  15. package/src/core/ontology/registry.ts +0 -209
  16. package/src/core/ontology/types.ts +0 -124
  17. package/src/core/ontology/validator.ts +0 -382
  18. package/src/core/persist/backend.ts +0 -74
  19. package/src/core/persist/sqlite-backend.ts +0 -298
  20. package/src/core/plugins/index.ts +0 -17
  21. package/src/core/plugins/registry.ts +0 -322
  22. package/src/core/plugins/types.ts +0 -126
  23. package/src/core/query/datalog.ts +0 -188
  24. package/src/core/query/engine.ts +0 -370
  25. package/src/core/query/index.ts +0 -34
  26. package/src/core/query/parser.ts +0 -481
  27. package/src/core/query/types.ts +0 -200
  28. package/src/core/store/eav-store.ts +0 -467
  29. package/src/decisions/auto-capture.ts +0 -136
  30. package/src/decisions/hooks.ts +0 -163
  31. package/src/decisions/index.ts +0 -261
  32. package/src/decisions/types.ts +0 -103
  33. package/src/embeddings/auto-embed.ts +0 -248
  34. package/src/embeddings/chunker.ts +0 -327
  35. package/src/embeddings/index.ts +0 -48
  36. package/src/embeddings/model.ts +0 -112
  37. package/src/embeddings/search.ts +0 -305
  38. package/src/embeddings/store.ts +0 -313
  39. package/src/embeddings/types.ts +0 -92
  40. package/src/engine.ts +0 -1125
  41. package/src/garden/cluster.ts +0 -330
  42. package/src/garden/garden.ts +0 -306
  43. package/src/garden/index.ts +0 -29
  44. package/src/git/git-exporter.ts +0 -286
  45. package/src/git/git-importer.ts +0 -329
  46. package/src/git/git-reader.ts +0 -189
  47. package/src/git/index.ts +0 -22
  48. package/src/identity/governance.ts +0 -211
  49. package/src/identity/identity.ts +0 -224
  50. package/src/identity/index.ts +0 -30
  51. package/src/identity/signing-middleware.ts +0 -97
  52. package/src/index.ts +0 -29
  53. package/src/links/index.ts +0 -49
  54. package/src/links/lifecycle.ts +0 -400
  55. package/src/links/parser.ts +0 -484
  56. package/src/links/ref-index.ts +0 -186
  57. package/src/links/resolver.ts +0 -314
  58. package/src/links/types.ts +0 -108
  59. package/src/mcp/index.ts +0 -22
  60. package/src/mcp/server.ts +0 -1278
  61. package/src/semantic/csharp-parser.ts +0 -493
  62. package/src/semantic/go-parser.ts +0 -585
  63. package/src/semantic/index.ts +0 -34
  64. package/src/semantic/java-parser.ts +0 -456
  65. package/src/semantic/python-parser.ts +0 -659
  66. package/src/semantic/ruby-parser.ts +0 -446
  67. package/src/semantic/rust-parser.ts +0 -784
  68. package/src/semantic/semantic-merge.ts +0 -210
  69. package/src/semantic/ts-parser.ts +0 -681
  70. package/src/semantic/types.ts +0 -175
  71. package/src/sync/http-transport.ts +0 -144
  72. package/src/sync/index.ts +0 -43
  73. package/src/sync/memory-transport.ts +0 -66
  74. package/src/sync/multi-repo.ts +0 -200
  75. package/src/sync/reconciler.ts +0 -237
  76. package/src/sync/sync-engine.ts +0 -258
  77. package/src/sync/types.ts +0 -104
  78. package/src/sync/ws-transport.ts +0 -145
  79. package/src/ui/client.html +0 -695
  80. package/src/ui/server.ts +0 -419
  81. package/src/vcs/blob-store.ts +0 -124
  82. package/src/vcs/branch.ts +0 -150
  83. package/src/vcs/checkpoint.ts +0 -64
  84. package/src/vcs/decompose.ts +0 -469
  85. package/src/vcs/diff.ts +0 -409
  86. package/src/vcs/engine-context.ts +0 -26
  87. package/src/vcs/index.ts +0 -23
  88. package/src/vcs/issue.ts +0 -800
  89. package/src/vcs/merge.ts +0 -425
  90. package/src/vcs/milestone.ts +0 -124
  91. package/src/vcs/ops.ts +0 -59
  92. package/src/vcs/types.ts +0 -213
  93. package/src/vcs/vcs-middleware.ts +0 -81
  94. package/src/watcher/fs-watcher.ts +0 -255
  95. package/src/watcher/index.ts +0 -9
  96. package/src/watcher/ingestion.ts +0 -116
@@ -1,175 +0,0 @@
1
- /**
2
- * Semantic Patching — Type Definitions
3
- *
4
- * DESIGN.md §4.1–4.4 — Pillar 2: Semantic Patching.
5
- * Types for parser adapters, AST entities, parse results,
6
- * semantic patches, and structured merge conflicts.
7
- */
8
-
9
- // ---------------------------------------------------------------------------
10
- // AST Entities
11
- // ---------------------------------------------------------------------------
12
-
13
- export interface ASTEntity {
14
- /** Stable ID derived from structural signature (name + kind + scope path). */
15
- id: string;
16
- /** Entity type. */
17
- kind: ASTEntityKind;
18
- /** Human-readable name. */
19
- name: string;
20
- /** Scope path for disambiguation (e.g. 'MyClass.myMethod'). */
21
- scopePath: string;
22
- /** Byte range in source for roundtrip (start, end). */
23
- span: [number, number];
24
- /** Raw source text of this declaration. */
25
- rawText: string;
26
- /** Structural signature for identity matching (excludes whitespace/comments). */
27
- signature: string;
28
- /** Child entities (nested functions, inner classes, etc.). */
29
- children: ASTEntity[];
30
- }
31
-
32
- export type ASTEntityKind =
33
- | 'FunctionDef'
34
- | 'ClassDef'
35
- | 'InterfaceDef'
36
- | 'TypeAlias'
37
- | 'EnumDef'
38
- | 'VariableDecl'
39
- | 'MethodDef'
40
- | 'PropertyDef'
41
- | 'Constructor'
42
- | 'Unknown';
43
-
44
- // ---------------------------------------------------------------------------
45
- // Import / Export Relations
46
- // ---------------------------------------------------------------------------
47
-
48
- export interface ImportRelation {
49
- source: string;
50
- specifiers: string[];
51
- isDefault: boolean;
52
- isNamespace: boolean;
53
- rawText: string;
54
- span: [number, number];
55
- }
56
-
57
- export interface ExportRelation {
58
- name: string;
59
- isDefault: boolean;
60
- source?: string;
61
- rawText: string;
62
- span: [number, number];
63
- }
64
-
65
- // ---------------------------------------------------------------------------
66
- // Parse Result
67
- // ---------------------------------------------------------------------------
68
-
69
- export interface ParseResult {
70
- /** The file entity this parse belongs to. */
71
- fileEntityId: string;
72
- /** File path. */
73
- filePath: string;
74
- /** Language identifier. */
75
- language: string;
76
- /** Top-level declarations found in the file. */
77
- declarations: ASTEntity[];
78
- /** Import relationships. */
79
- imports: ImportRelation[];
80
- /** Export relationships. */
81
- exports: ExportRelation[];
82
- }
83
-
84
- // ---------------------------------------------------------------------------
85
- // Parser Adapter Interface
86
- // ---------------------------------------------------------------------------
87
-
88
- export interface ParserAdapter {
89
- /** Languages this adapter supports. */
90
- languages: string[];
91
- /** Parse a source file into AST-level entities. */
92
- parse(content: string, filePath: string): ParseResult;
93
- /** Compute semantic patches between two parse results. */
94
- diff(oldResult: ParseResult, newResult: ParseResult): SemanticPatch[];
95
- }
96
-
97
- // ---------------------------------------------------------------------------
98
- // Semantic Patch Types
99
- // ---------------------------------------------------------------------------
100
-
101
- export type SemanticPatch =
102
- | { kind: 'symbolAdd'; entity: ASTEntity }
103
- | { kind: 'symbolRemove'; entityId: string; entityName: string }
104
- | {
105
- kind: 'symbolModify';
106
- entityId: string;
107
- entityName: string;
108
- oldSignature: string;
109
- newSignature: string;
110
- oldRawText: string;
111
- newRawText: string;
112
- }
113
- | {
114
- kind: 'symbolRename';
115
- entityId: string;
116
- oldName: string;
117
- newName: string;
118
- }
119
- | {
120
- kind: 'symbolMove';
121
- entityId: string;
122
- entityName: string;
123
- oldFile: string;
124
- newFile: string;
125
- }
126
- | {
127
- kind: 'importAdd';
128
- fileId: string;
129
- source: string;
130
- specifiers: string[];
131
- rawText: string;
132
- }
133
- | {
134
- kind: 'importRemove';
135
- fileId: string;
136
- source: string;
137
- }
138
- | {
139
- kind: 'importModify';
140
- fileId: string;
141
- source: string;
142
- oldSpecifiers: string[];
143
- newSpecifiers: string[];
144
- }
145
- | {
146
- kind: 'exportAdd';
147
- fileId: string;
148
- name: string;
149
- rawText: string;
150
- }
151
- | {
152
- kind: 'exportRemove';
153
- fileId: string;
154
- name: string;
155
- };
156
-
157
- // ---------------------------------------------------------------------------
158
- // Semantic Merge Conflict
159
- // ---------------------------------------------------------------------------
160
-
161
- export interface SemanticMergeConflict {
162
- entityId: string;
163
- entityName: string;
164
- entityKind: string;
165
- filePath: string;
166
- ours: SemanticPatch;
167
- theirs: SemanticPatch;
168
- suggestion?: 'accept-ours' | 'accept-theirs' | 'combine';
169
- }
170
-
171
- export interface SemanticMergeResult {
172
- clean: boolean;
173
- patches: SemanticPatch[];
174
- conflicts: SemanticMergeConflict[];
175
- }
@@ -1,144 +0,0 @@
1
- /**
2
- * HTTP Sync Transport
3
- *
4
- * Implements SyncTransport over HTTP for network-based peer sync.
5
- * Uses a simple JSON REST protocol:
6
- * POST /sync/message — send a sync message
7
- * GET /sync/peers — list connected peers
8
- *
9
- * The server side is a lightweight Bun HTTP server.
10
- * The client side uses fetch() for outbound messages.
11
- *
12
- * @module trellis/sync
13
- */
14
-
15
- import type { SyncTransport, SyncMessage, PeerId } from './types.js';
16
-
17
- // ---------------------------------------------------------------------------
18
- // HTTP Transport (Client)
19
- // ---------------------------------------------------------------------------
20
-
21
- export class HttpSyncTransport implements SyncTransport {
22
- private localPeerId: string;
23
- private peerUrls: Map<string, string> = new Map();
24
- private messageHandler: ((msg: SyncMessage) => void) | null = null;
25
- private knownPeers: Map<string, PeerId> = new Map();
26
-
27
- constructor(localPeerId: string) {
28
- this.localPeerId = localPeerId;
29
- }
30
-
31
- /**
32
- * Add a remote peer by URL (e.g. "http://192.168.1.10:4200").
33
- */
34
- addPeer(peerId: string, url: string, name?: string): void {
35
- this.peerUrls.set(peerId, url);
36
- this.knownPeers.set(peerId, {
37
- id: peerId,
38
- name: name ?? peerId,
39
- lastSeen: new Date().toISOString(),
40
- });
41
- }
42
-
43
- /**
44
- * Remove a remote peer.
45
- */
46
- removePeer(peerId: string): void {
47
- this.peerUrls.delete(peerId);
48
- this.knownPeers.delete(peerId);
49
- }
50
-
51
- async send(peerId: string, message: SyncMessage): Promise<void> {
52
- const url = this.peerUrls.get(peerId);
53
- if (!url) throw new Error(`Unknown peer "${peerId}". Add it with addPeer() first.`);
54
-
55
- const resp = await fetch(`${url}/sync/message`, {
56
- method: 'POST',
57
- headers: { 'Content-Type': 'application/json' },
58
- body: JSON.stringify(message),
59
- });
60
-
61
- if (!resp.ok) {
62
- throw new Error(`Sync message to ${peerId} failed: ${resp.status} ${resp.statusText}`);
63
- }
64
-
65
- // Check if the response contains a reply message
66
- const contentType = resp.headers.get('content-type');
67
- if (contentType?.includes('application/json')) {
68
- const reply = await resp.json();
69
- if (reply && reply.type && this.messageHandler) {
70
- this.messageHandler(reply as SyncMessage);
71
- }
72
- }
73
- }
74
-
75
- onMessage(handler: (message: SyncMessage) => void): void {
76
- this.messageHandler = handler;
77
- }
78
-
79
- /**
80
- * Receive a message (called by the HTTP server handler).
81
- */
82
- receiveMessage(message: SyncMessage): void {
83
- // Track peer
84
- this.knownPeers.set(message.peerId, {
85
- id: message.peerId,
86
- name: message.peerId,
87
- lastSeen: new Date().toISOString(),
88
- });
89
-
90
- if (this.messageHandler) {
91
- this.messageHandler(message);
92
- }
93
- }
94
-
95
- peers(): PeerId[] {
96
- return [...this.knownPeers.values()];
97
- }
98
-
99
- getLocalPeerId(): string {
100
- return this.localPeerId;
101
- }
102
- }
103
-
104
- // ---------------------------------------------------------------------------
105
- // HTTP Sync Server (creates Bun.serve handler)
106
- // ---------------------------------------------------------------------------
107
-
108
- export interface HttpSyncServerConfig {
109
- port: number;
110
- transport: HttpSyncTransport;
111
- }
112
-
113
- /**
114
- * Create a Bun-compatible HTTP request handler for sync messages.
115
- * Can be used with Bun.serve() or as middleware.
116
- */
117
- export function createSyncHandler(transport: HttpSyncTransport): (req: Request) => Response | null {
118
- return (req: Request): Response | null => {
119
- const url = new URL(req.url);
120
-
121
- if (url.pathname === '/sync/message' && req.method === 'POST') {
122
- // Handle async parsing synchronously for Bun
123
- return new Response(
124
- req.json().then((body: any) => {
125
- transport.receiveMessage(body as SyncMessage);
126
- return JSON.stringify({ ok: true });
127
- }) as any,
128
- { headers: { 'Content-Type': 'application/json' } },
129
- );
130
- }
131
-
132
- if (url.pathname === '/sync/peers' && req.method === 'GET') {
133
- return new Response(
134
- JSON.stringify({
135
- localPeerId: transport.getLocalPeerId(),
136
- peers: transport.peers(),
137
- }),
138
- { headers: { 'Content-Type': 'application/json' } },
139
- );
140
- }
141
-
142
- return null; // Not a sync route
143
- };
144
- }
package/src/sync/index.ts DELETED
@@ -1,43 +0,0 @@
1
- /**
2
- * Sync Module — Public Surface
3
- *
4
- * @module sync
5
- *
6
- * Re-exports the CRDT {@link reconcile|reconciler}, {@link SyncEngine} for
7
- * the have→want→ops→ack protocol, and {@link MemoryTransport} for testing.
8
- * Branch policies control whether sync uses linear (fast-forward) or
9
- * CRDT (concurrent append) mode.
10
- *
11
- * @see DESIGN.md §3.5 for the branch concurrency model.
12
- */
13
-
14
- export type {
15
- PeerId,
16
- SyncMessage,
17
- SyncHaveMessage,
18
- SyncWantMessage,
19
- SyncOpsMessage,
20
- SyncAckMessage,
21
- SyncState,
22
- BranchPolicy,
23
- SyncTransport,
24
- } from './types.js';
25
-
26
- export { reconcile, findForkPoint } from './reconciler.js';
27
-
28
- export type { ReconcileResult, ReconcileConflict } from './reconciler.js';
29
-
30
- export { SyncEngine } from './sync-engine.js';
31
-
32
- export { MemoryTransport } from './memory-transport.js';
33
-
34
- export { HttpSyncTransport, createSyncHandler } from './http-transport.js';
35
-
36
- export { WebSocketSyncTransport } from './ws-transport.js';
37
-
38
- export {
39
- MultiRepoManager,
40
- parseCrossRepoRef,
41
- formatCrossRepoRef,
42
- } from './multi-repo.js';
43
- export type { LinkedRepo, CrossRepoRef } from './multi-repo.js';
@@ -1,66 +0,0 @@
1
- /**
2
- * In-Memory Sync Transport
3
- *
4
- * A simple in-memory transport for testing and local multi-engine sync.
5
- * Messages are delivered synchronously between connected peers.
6
- */
7
-
8
- import type { SyncTransport, SyncMessage, PeerId } from './types.js';
9
-
10
- // ---------------------------------------------------------------------------
11
- // In-memory transport
12
- // ---------------------------------------------------------------------------
13
-
14
- export class MemoryTransport implements SyncTransport {
15
- private peerId: string;
16
- private peerName: string;
17
- private handlers: ((message: SyncMessage) => void)[] = [];
18
- private connectedPeers: Map<string, MemoryTransport> = new Map();
19
-
20
- constructor(peerId: string, peerName: string = peerId) {
21
- this.peerId = peerId;
22
- this.peerName = peerName;
23
- }
24
-
25
- /**
26
- * Connect two transports so they can exchange messages.
27
- */
28
- static connect(a: MemoryTransport, b: MemoryTransport): void {
29
- a.connectedPeers.set(b.peerId, b);
30
- b.connectedPeers.set(a.peerId, a);
31
- }
32
-
33
- /**
34
- * Disconnect two transports.
35
- */
36
- static disconnect(a: MemoryTransport, b: MemoryTransport): void {
37
- a.connectedPeers.delete(b.peerId);
38
- b.connectedPeers.delete(a.peerId);
39
- }
40
-
41
- async send(peerId: string, message: SyncMessage): Promise<void> {
42
- const peer = this.connectedPeers.get(peerId);
43
- if (!peer) {
44
- throw new Error(`Peer not connected: ${peerId}`);
45
- }
46
- // Deliver to peer's handlers
47
- for (const handler of peer.handlers) {
48
- handler(message);
49
- }
50
- }
51
-
52
- onMessage(handler: (message: SyncMessage) => void): void {
53
- this.handlers.push(handler);
54
- }
55
-
56
- peers(): PeerId[] {
57
- return [...this.connectedPeers.entries()].map(([id, transport]) => ({
58
- id,
59
- name: transport.peerName,
60
- }));
61
- }
62
-
63
- getPeerId(): string {
64
- return this.peerId;
65
- }
66
- }
@@ -1,200 +0,0 @@
1
- /**
2
- * Multi-Repo Linking — Cross-repo entity references.
3
- *
4
- * Enables entities in one Trellis repo to reference entities in another.
5
- * Linked repos are registered with a local alias and remote path/URL.
6
- * Cross-repo references use the format: `@alias:entityId`
7
- *
8
- * @module trellis/sync
9
- */
10
-
11
- import type { TrellisKernel } from '../core/kernel/trellis-kernel.js';
12
- import type { Fact, Link } from '../core/store/eav-store.js';
13
-
14
- // ---------------------------------------------------------------------------
15
- // Types
16
- // ---------------------------------------------------------------------------
17
-
18
- export interface LinkedRepo {
19
- /** Local alias for the remote repo (e.g. "backend", "shared-lib"). */
20
- alias: string;
21
- /** Path or URL to the remote repo. */
22
- location: string;
23
- /** Optional human-readable description. */
24
- description?: string;
25
- /** When this link was established. */
26
- linkedAt: string;
27
- /** Last sync timestamp. */
28
- lastSyncedAt?: string;
29
- }
30
-
31
- export interface CrossRepoRef {
32
- /** The repo alias. */
33
- repoAlias: string;
34
- /** The entity ID in the remote repo. */
35
- entityId: string;
36
- }
37
-
38
- // ---------------------------------------------------------------------------
39
- // Multi-Repo Manager
40
- // ---------------------------------------------------------------------------
41
-
42
- export class MultiRepoManager {
43
- private kernel: TrellisKernel;
44
-
45
- constructor(kernel: TrellisKernel) {
46
- this.kernel = kernel;
47
- }
48
-
49
- /**
50
- * Link a remote repository.
51
- */
52
- async linkRepo(alias: string, location: string, description?: string): Promise<void> {
53
- const id = `repo:${alias}`;
54
- const existing = this.kernel.getEntity(id);
55
- if (existing) {
56
- throw new Error(`Repo alias "${alias}" is already linked to "${existing.facts.find(f => f.a === 'location')?.v}".`);
57
- }
58
-
59
- await this.kernel.createEntity(id, 'LinkedRepo', {
60
- alias,
61
- location,
62
- ...(description ? { description } : {}),
63
- linkedAt: new Date().toISOString(),
64
- });
65
- }
66
-
67
- /**
68
- * Unlink a remote repository.
69
- */
70
- async unlinkRepo(alias: string): Promise<void> {
71
- const id = `repo:${alias}`;
72
- await this.kernel.deleteEntity(id);
73
- }
74
-
75
- /**
76
- * List all linked repos.
77
- */
78
- listLinkedRepos(): LinkedRepo[] {
79
- const entities = this.kernel.listEntities('LinkedRepo');
80
- return entities.map((e) => {
81
- const get = (a: string) => e.facts.find((f) => f.a === a)?.v;
82
- return {
83
- alias: String(get('alias') ?? ''),
84
- location: String(get('location') ?? ''),
85
- description: get('description') as string | undefined,
86
- linkedAt: String(get('linkedAt') ?? ''),
87
- lastSyncedAt: get('lastSyncedAt') as string | undefined,
88
- };
89
- });
90
- }
91
-
92
- /**
93
- * Get a linked repo by alias.
94
- */
95
- getLinkedRepo(alias: string): LinkedRepo | null {
96
- const entity = this.kernel.getEntity(`repo:${alias}`);
97
- if (!entity) return null;
98
- const get = (a: string) => entity.facts.find((f) => f.a === a)?.v;
99
- return {
100
- alias: String(get('alias') ?? ''),
101
- location: String(get('location') ?? ''),
102
- description: get('description') as string | undefined,
103
- linkedAt: String(get('linkedAt') ?? ''),
104
- lastSyncedAt: get('lastSyncedAt') as string | undefined,
105
- };
106
- }
107
-
108
- /**
109
- * Create a cross-repo link: entity in this repo → entity in remote repo.
110
- */
111
- async addCrossRepoLink(
112
- sourceEntityId: string,
113
- attribute: string,
114
- targetRepoAlias: string,
115
- targetEntityId: string,
116
- ): Promise<void> {
117
- const repo = this.getLinkedRepo(targetRepoAlias);
118
- if (!repo) {
119
- throw new Error(`Repo alias "${targetRepoAlias}" is not linked. Use linkRepo() first.`);
120
- }
121
-
122
- const crossRef = `@${targetRepoAlias}:${targetEntityId}`;
123
- await this.kernel.addLink(sourceEntityId, attribute, crossRef);
124
- }
125
-
126
- /**
127
- * Remove a cross-repo link.
128
- */
129
- async removeCrossRepoLink(
130
- sourceEntityId: string,
131
- attribute: string,
132
- targetRepoAlias: string,
133
- targetEntityId: string,
134
- ): Promise<void> {
135
- const crossRef = `@${targetRepoAlias}:${targetEntityId}`;
136
- await this.kernel.removeLink(sourceEntityId, attribute, crossRef);
137
- }
138
-
139
- /**
140
- * Find all cross-repo references from a given entity.
141
- */
142
- getCrossRepoLinks(entityId: string): Array<{ attribute: string; ref: CrossRepoRef }> {
143
- const store = this.kernel.getStore();
144
- const links = store.getLinksByEntity(entityId);
145
- const results: Array<{ attribute: string; ref: CrossRepoRef }> = [];
146
-
147
- for (const link of links) {
148
- if (link.e1 !== entityId) continue;
149
- const parsed = parseCrossRepoRef(link.e2);
150
- if (parsed) {
151
- results.push({ attribute: link.a, ref: parsed });
152
- }
153
- }
154
-
155
- return results;
156
- }
157
-
158
- /**
159
- * Find all cross-repo references pointing to a specific remote entity.
160
- */
161
- findReferencesTo(repoAlias: string, entityId: string): Link[] {
162
- const crossRef = `@${repoAlias}:${entityId}`;
163
- const store = this.kernel.getStore();
164
- return store.getAllLinks().filter((l) => l.e2 === crossRef);
165
- }
166
-
167
- /**
168
- * Update the lastSyncedAt timestamp for a linked repo.
169
- */
170
- async markSynced(alias: string): Promise<void> {
171
- await this.kernel.updateEntity(`repo:${alias}`, {
172
- lastSyncedAt: new Date().toISOString(),
173
- });
174
- }
175
- }
176
-
177
- // ---------------------------------------------------------------------------
178
- // Helpers
179
- // ---------------------------------------------------------------------------
180
-
181
- /**
182
- * Parse a cross-repo reference string.
183
- * Format: `@alias:entityId`
184
- */
185
- export function parseCrossRepoRef(ref: string): CrossRepoRef | null {
186
- if (!ref.startsWith('@')) return null;
187
- const colonIdx = ref.indexOf(':', 1);
188
- if (colonIdx === -1) return null;
189
- return {
190
- repoAlias: ref.slice(1, colonIdx),
191
- entityId: ref.slice(colonIdx + 1),
192
- };
193
- }
194
-
195
- /**
196
- * Format a cross-repo reference string.
197
- */
198
- export function formatCrossRepoRef(repoAlias: string, entityId: string): string {
199
- return `@${repoAlias}:${entityId}`;
200
- }