trellis 1.0.8 → 2.0.5

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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +533 -82
  3. package/bin/trellis.mjs +2 -0
  4. package/dist/cli/index.js +4718 -0
  5. package/dist/core/index.js +12 -0
  6. package/dist/decisions/index.js +19 -0
  7. package/dist/embeddings/index.js +43 -0
  8. package/dist/index-1j1anhmr.js +4038 -0
  9. package/dist/index-3s0eak0p.js +1556 -0
  10. package/dist/index-8pce39mh.js +272 -0
  11. package/dist/index-a76rekgs.js +67 -0
  12. package/dist/index-cy9k1g6v.js +684 -0
  13. package/dist/index-fd4e26s4.js +69 -0
  14. package/dist/{store/eav-store.js → index-gkvhzm9f.js} +4 -6
  15. package/dist/index-gnw8d7d6.js +51 -0
  16. package/dist/index-vkpkfwhq.js +817 -0
  17. package/dist/index.js +118 -2876
  18. package/dist/links/index.js +55 -0
  19. package/dist/transformers-m9je15kg.js +32491 -0
  20. package/dist/vcs/index.js +110 -0
  21. package/logo.png +0 -0
  22. package/logo.svg +9 -0
  23. package/package.json +79 -76
  24. package/src/cli/index.ts +2340 -0
  25. package/src/core/index.ts +35 -0
  26. package/src/core/kernel/middleware.ts +44 -0
  27. package/src/core/persist/backend.ts +64 -0
  28. package/src/core/store/eav-store.ts +467 -0
  29. package/src/decisions/auto-capture.ts +136 -0
  30. package/src/decisions/hooks.ts +163 -0
  31. package/src/decisions/index.ts +261 -0
  32. package/src/decisions/types.ts +103 -0
  33. package/src/embeddings/chunker.ts +327 -0
  34. package/src/embeddings/index.ts +41 -0
  35. package/src/embeddings/model.ts +95 -0
  36. package/src/embeddings/search.ts +305 -0
  37. package/src/embeddings/store.ts +313 -0
  38. package/src/embeddings/types.ts +85 -0
  39. package/src/engine.ts +1083 -0
  40. package/src/garden/cluster.ts +330 -0
  41. package/src/garden/garden.ts +306 -0
  42. package/src/garden/index.ts +29 -0
  43. package/src/git/git-exporter.ts +286 -0
  44. package/src/git/git-importer.ts +329 -0
  45. package/src/git/git-reader.ts +189 -0
  46. package/src/git/index.ts +22 -0
  47. package/src/identity/governance.ts +211 -0
  48. package/src/identity/identity.ts +224 -0
  49. package/src/identity/index.ts +30 -0
  50. package/src/identity/signing-middleware.ts +97 -0
  51. package/src/index.ts +20 -0
  52. package/src/links/index.ts +49 -0
  53. package/src/links/lifecycle.ts +400 -0
  54. package/src/links/parser.ts +484 -0
  55. package/src/links/ref-index.ts +186 -0
  56. package/src/links/resolver.ts +314 -0
  57. package/src/links/types.ts +108 -0
  58. package/src/mcp/index.ts +22 -0
  59. package/src/mcp/server.ts +1278 -0
  60. package/src/semantic/csharp-parser.ts +493 -0
  61. package/src/semantic/go-parser.ts +585 -0
  62. package/src/semantic/index.ts +34 -0
  63. package/src/semantic/java-parser.ts +456 -0
  64. package/src/semantic/python-parser.ts +659 -0
  65. package/src/semantic/ruby-parser.ts +446 -0
  66. package/src/semantic/rust-parser.ts +784 -0
  67. package/src/semantic/semantic-merge.ts +210 -0
  68. package/src/semantic/ts-parser.ts +681 -0
  69. package/src/semantic/types.ts +175 -0
  70. package/src/sync/index.ts +32 -0
  71. package/src/sync/memory-transport.ts +66 -0
  72. package/src/sync/reconciler.ts +237 -0
  73. package/src/sync/sync-engine.ts +258 -0
  74. package/src/sync/types.ts +104 -0
  75. package/src/vcs/blob-store.ts +124 -0
  76. package/src/vcs/branch.ts +150 -0
  77. package/src/vcs/checkpoint.ts +64 -0
  78. package/src/vcs/decompose.ts +469 -0
  79. package/src/vcs/diff.ts +409 -0
  80. package/src/vcs/engine-context.ts +26 -0
  81. package/src/vcs/index.ts +23 -0
  82. package/src/vcs/issue.ts +800 -0
  83. package/src/vcs/merge.ts +425 -0
  84. package/src/vcs/milestone.ts +124 -0
  85. package/src/vcs/ops.ts +59 -0
  86. package/src/vcs/types.ts +213 -0
  87. package/src/vcs/vcs-middleware.ts +81 -0
  88. package/src/watcher/fs-watcher.ts +217 -0
  89. package/src/watcher/index.ts +9 -0
  90. package/src/watcher/ingestion.ts +116 -0
  91. package/dist/ai/index.js +0 -688
  92. package/dist/cli/server.js +0 -3321
  93. package/dist/cli/tql.js +0 -5282
  94. package/dist/client/tql-client.js +0 -108
  95. package/dist/graph/index.js +0 -2248
  96. package/dist/kernel/logic-middleware.js +0 -179
  97. package/dist/kernel/middleware.js +0 -0
  98. package/dist/kernel/operations.js +0 -32
  99. package/dist/kernel/schema-middleware.js +0 -34
  100. package/dist/kernel/security-middleware.js +0 -53
  101. package/dist/kernel/trellis-kernel.js +0 -2239
  102. package/dist/kernel/workspace.js +0 -91
  103. package/dist/persist/backend.js +0 -0
  104. package/dist/persist/sqlite-backend.js +0 -123
  105. package/dist/query/index.js +0 -1643
  106. package/dist/server/index.js +0 -3309
  107. package/dist/workflows/index.js +0 -3160
@@ -0,0 +1,175 @@
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
+ }
@@ -0,0 +1,32 @@
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';
@@ -0,0 +1,66 @@
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
+ }
@@ -0,0 +1,237 @@
1
+ /**
2
+ * CRDT Reconciler
3
+ *
4
+ * DESIGN.md §10.5 — Merges divergent op streams using causal ordering.
5
+ * Each device maintains its own causal chain. The reconciler merges
6
+ * divergent chains by:
7
+ * 1. Finding the common ancestor (fork point)
8
+ * 2. Collecting ops unique to each side
9
+ * 3. Topologically sorting the combined set by causal dependencies
10
+ * 4. Detecting conflicts using patch commutativity (§4.4)
11
+ */
12
+
13
+ import type { VcsOp } from '../vcs/types.js';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Types
17
+ // ---------------------------------------------------------------------------
18
+
19
+ export interface ReconcileResult {
20
+ /** The merged op stream in causal order. */
21
+ merged: VcsOp[];
22
+ /** Ops that were only on side A. */
23
+ uniqueToA: VcsOp[];
24
+ /** Ops that were only on side B. */
25
+ uniqueToB: VcsOp[];
26
+ /** Common ancestor op hash (fork point). */
27
+ forkPoint: string | null;
28
+ /** Whether the merge was clean (no causal conflicts). */
29
+ clean: boolean;
30
+ /** Conflicting op pairs (both modify same file without commutativity). */
31
+ conflicts: ReconcileConflict[];
32
+ }
33
+
34
+ export interface ReconcileConflict {
35
+ opA: VcsOp;
36
+ opB: VcsOp;
37
+ filePath: string;
38
+ reason: string;
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Core reconciliation
43
+ // ---------------------------------------------------------------------------
44
+
45
+ /**
46
+ * Find the common ancestor (fork point) of two op streams.
47
+ * Returns the hash of the last op that appears in both streams.
48
+ */
49
+ export function findForkPoint(opsA: VcsOp[], opsB: VcsOp[]): string | null {
50
+ const hashesB = new Set(opsB.map((o) => o.hash));
51
+ let forkPoint: string | null = null;
52
+
53
+ for (const op of opsA) {
54
+ if (hashesB.has(op.hash)) {
55
+ forkPoint = op.hash;
56
+ }
57
+ }
58
+
59
+ return forkPoint;
60
+ }
61
+
62
+ /**
63
+ * Reconcile two divergent op streams into a single merged stream.
64
+ *
65
+ * Algorithm:
66
+ * 1. Find the fork point (last common op)
67
+ * 2. Split each stream into shared prefix + unique suffix
68
+ * 3. Check for conflicts in the unique portions
69
+ * 4. Interleave unique ops in causal (timestamp) order
70
+ */
71
+ export function reconcile(opsA: VcsOp[], opsB: VcsOp[]): ReconcileResult {
72
+ const forkPoint = findForkPoint(opsA, opsB);
73
+
74
+ // Split into shared prefix and unique suffixes
75
+ const hashesA = new Set(opsA.map((o) => o.hash));
76
+ const hashesB = new Set(opsB.map((o) => o.hash));
77
+
78
+ const shared: VcsOp[] = [];
79
+ const uniqueToA: VcsOp[] = [];
80
+ const uniqueToB: VcsOp[] = [];
81
+
82
+ for (const op of opsA) {
83
+ if (hashesB.has(op.hash)) {
84
+ shared.push(op);
85
+ } else {
86
+ uniqueToA.push(op);
87
+ }
88
+ }
89
+
90
+ for (const op of opsB) {
91
+ if (!hashesA.has(op.hash)) {
92
+ uniqueToB.push(op);
93
+ }
94
+ }
95
+
96
+ // If one side has no unique ops, it's a fast-forward
97
+ if (uniqueToA.length === 0) {
98
+ return {
99
+ merged: [...shared, ...uniqueToB],
100
+ uniqueToA: [],
101
+ uniqueToB,
102
+ forkPoint,
103
+ clean: true,
104
+ conflicts: [],
105
+ };
106
+ }
107
+
108
+ if (uniqueToB.length === 0) {
109
+ return {
110
+ merged: [...shared, ...uniqueToA],
111
+ uniqueToA,
112
+ uniqueToB: [],
113
+ forkPoint,
114
+ clean: true,
115
+ conflicts: [],
116
+ };
117
+ }
118
+
119
+ // Both sides diverged — detect conflicts
120
+ const conflicts = detectConflicts(uniqueToA, uniqueToB);
121
+
122
+ // Merge unique ops by timestamp (causal ordering)
123
+ const interleaved = interleaveByTimestamp(uniqueToA, uniqueToB);
124
+
125
+ return {
126
+ merged: [...shared, ...interleaved],
127
+ uniqueToA,
128
+ uniqueToB,
129
+ forkPoint,
130
+ clean: conflicts.length === 0,
131
+ conflicts,
132
+ };
133
+ }
134
+
135
+ // ---------------------------------------------------------------------------
136
+ // Conflict detection
137
+ // ---------------------------------------------------------------------------
138
+
139
+ /** VcsOp kinds that represent file-level mutations. */
140
+ const FILE_MUTATION_KINDS = new Set([
141
+ 'vcs:fileAdd',
142
+ 'vcs:fileModify',
143
+ 'vcs:fileDelete',
144
+ 'vcs:fileRename',
145
+ ]);
146
+
147
+ /**
148
+ * Detect conflicts between two sets of unique ops.
149
+ * Two ops conflict when they both mutate the same file.
150
+ */
151
+ function detectConflicts(
152
+ uniqueA: VcsOp[],
153
+ uniqueB: VcsOp[],
154
+ ): ReconcileConflict[] {
155
+ const conflicts: ReconcileConflict[] = [];
156
+
157
+ // Index A's file mutations
158
+ const aMutations = new Map<string, VcsOp[]>();
159
+ for (const op of uniqueA) {
160
+ if (!FILE_MUTATION_KINDS.has(op.kind) || !op.vcs?.filePath) continue;
161
+ const path = op.vcs.filePath;
162
+ if (!aMutations.has(path)) aMutations.set(path, []);
163
+ aMutations.get(path)!.push(op);
164
+ }
165
+
166
+ // Check B's file mutations against A's
167
+ for (const op of uniqueB) {
168
+ if (!FILE_MUTATION_KINDS.has(op.kind) || !op.vcs?.filePath) continue;
169
+ const path = op.vcs.filePath;
170
+ const aOps = aMutations.get(path);
171
+ if (!aOps) continue;
172
+
173
+ for (const aOp of aOps) {
174
+ // Same file modified by both sides
175
+ if (aOp.kind === 'vcs:fileModify' && op.kind === 'vcs:fileModify') {
176
+ conflicts.push({
177
+ opA: aOp,
178
+ opB: op,
179
+ filePath: path,
180
+ reason: `Both sides modified ${path}`,
181
+ });
182
+ } else if (
183
+ (aOp.kind === 'vcs:fileDelete' && op.kind === 'vcs:fileModify') ||
184
+ (aOp.kind === 'vcs:fileModify' && op.kind === 'vcs:fileDelete')
185
+ ) {
186
+ conflicts.push({
187
+ opA: aOp,
188
+ opB: op,
189
+ filePath: path,
190
+ reason: `Delete/modify conflict on ${path}`,
191
+ });
192
+ } else if (aOp.kind === 'vcs:fileAdd' && op.kind === 'vcs:fileAdd') {
193
+ // Both added same file — conflict if different content
194
+ if (aOp.vcs?.contentHash !== op.vcs?.contentHash) {
195
+ conflicts.push({
196
+ opA: aOp,
197
+ opB: op,
198
+ filePath: path,
199
+ reason: `Both sides added ${path} with different content`,
200
+ });
201
+ }
202
+ }
203
+ }
204
+ }
205
+
206
+ return conflicts;
207
+ }
208
+
209
+ // ---------------------------------------------------------------------------
210
+ // Interleaving
211
+ // ---------------------------------------------------------------------------
212
+
213
+ /**
214
+ * Interleave two op arrays by timestamp, preserving causal ordering
215
+ * within each array.
216
+ */
217
+ function interleaveByTimestamp(a: VcsOp[], b: VcsOp[]): VcsOp[] {
218
+ const result: VcsOp[] = [];
219
+ let ai = 0;
220
+ let bi = 0;
221
+
222
+ while (ai < a.length && bi < b.length) {
223
+ const tA = new Date(a[ai].timestamp).getTime();
224
+ const tB = new Date(b[bi].timestamp).getTime();
225
+
226
+ if (tA <= tB) {
227
+ result.push(a[ai++]);
228
+ } else {
229
+ result.push(b[bi++]);
230
+ }
231
+ }
232
+
233
+ while (ai < a.length) result.push(a[ai++]);
234
+ while (bi < b.length) result.push(b[bi++]);
235
+
236
+ return result;
237
+ }