trellis 1.0.8 → 2.0.6

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 +564 -83
  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,314 @@
1
+ /**
2
+ * Entity Reference Resolver
3
+ *
4
+ * Resolves parsed EntityRefs to TrellisVCS entities by querying
5
+ * the EAV store, tracked files, semantic parser, milestones, and identities.
6
+ *
7
+ * @see TRL-12
8
+ */
9
+
10
+ import type { EntityRef, ResolvedRef, RefNamespace } from './types.js';
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Resolver Context — interface for engine capabilities
14
+ // ---------------------------------------------------------------------------
15
+
16
+ /**
17
+ * Abstract interface for the engine capabilities the resolver needs.
18
+ * This allows testing with mocks without a real TrellisVcsEngine.
19
+ */
20
+ export interface ResolverContext {
21
+ /** Get a tracked file by path. Returns true if the file exists in the op stream. */
22
+ hasTrackedFile(path: string): boolean;
23
+
24
+ /** Look up an issue by ID (e.g. "TRL-5"). Returns title if found. */
25
+ getIssueTitle(id: string): string | undefined;
26
+
27
+ /** Look up a milestone by ID or message fragment. Returns message if found. */
28
+ getMilestoneTitle(idOrMessage: string): string | undefined;
29
+
30
+ /** Check if a symbol exists in a file. Returns true if found. */
31
+ hasSymbol(filePath: string, symbolName: string): boolean;
32
+
33
+ /** Check if an identity/agent ID exists. Returns true if known. */
34
+ hasIdentity(id: string): boolean;
35
+
36
+ /** List all known agent IDs (from ops). */
37
+ getKnownAgentIds(): string[];
38
+
39
+ /** List all tracked file paths. */
40
+ getTrackedFilePaths(): string[];
41
+
42
+ /** List all issue IDs. */
43
+ getIssueIds(): string[];
44
+
45
+ /** List all milestone IDs. */
46
+ getMilestoneIds(): string[];
47
+
48
+ /** Check if a decision exists by ID (e.g. "DEC-1"). */
49
+ hasDecision(id: string): boolean;
50
+
51
+ /** Get a decision's tool name as a human-readable title. */
52
+ getDecisionTitle(id: string): string | undefined;
53
+
54
+ /** Get all symbol names for a file. */
55
+ getSymbolNames(filePath: string): string[];
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Public API
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /**
63
+ * Resolve a single EntityRef against the resolver context.
64
+ */
65
+ export function resolveRef(ref: EntityRef, ctx: ResolverContext): ResolvedRef {
66
+ switch (ref.namespace) {
67
+ case 'issue':
68
+ return resolveIssue(ref, ctx);
69
+ case 'file':
70
+ return resolveFile(ref, ctx);
71
+ case 'symbol':
72
+ return resolveSymbol(ref, ctx);
73
+ case 'identity':
74
+ return resolveIdentity(ref, ctx);
75
+ case 'milestone':
76
+ return resolveMilestone(ref, ctx);
77
+ case 'decision':
78
+ return resolveDecision(ref, ctx);
79
+ default:
80
+ return { ...ref, state: 'broken' };
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Resolve multiple EntityRefs in batch.
86
+ */
87
+ export function resolveRefs(
88
+ refs: EntityRef[],
89
+ ctx: ResolverContext,
90
+ ): ResolvedRef[] {
91
+ return refs.map((ref) => resolveRef(ref, ctx));
92
+ }
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Namespace-specific resolvers
96
+ // ---------------------------------------------------------------------------
97
+
98
+ function resolveIssue(ref: EntityRef, ctx: ResolverContext): ResolvedRef {
99
+ const title = ctx.getIssueTitle(ref.target);
100
+ if (title !== undefined) {
101
+ return {
102
+ ...ref,
103
+ state: 'resolved',
104
+ entityId: `issue:${ref.target}`,
105
+ title,
106
+ };
107
+ }
108
+ return { ...ref, state: 'broken' };
109
+ }
110
+
111
+ function resolveFile(ref: EntityRef, ctx: ResolverContext): ResolvedRef {
112
+ if (ctx.hasTrackedFile(ref.target)) {
113
+ return {
114
+ ...ref,
115
+ state: 'resolved',
116
+ entityId: `file:${ref.target}`,
117
+ title: ref.target,
118
+ };
119
+ }
120
+ return { ...ref, state: 'broken' };
121
+ }
122
+
123
+ function resolveSymbol(ref: EntityRef, ctx: ResolverContext): ResolvedRef {
124
+ // First check the file exists
125
+ if (!ctx.hasTrackedFile(ref.target)) {
126
+ return { ...ref, state: 'broken' };
127
+ }
128
+
129
+ // Then check the symbol exists in that file
130
+ if (ref.anchor && ctx.hasSymbol(ref.target, ref.anchor)) {
131
+ return {
132
+ ...ref,
133
+ state: 'resolved',
134
+ entityId: `symbol:${ref.target}#${ref.anchor}`,
135
+ title: `${ref.anchor} in ${ref.target}`,
136
+ };
137
+ }
138
+
139
+ // File exists but symbol not found
140
+ if (ref.anchor) {
141
+ return { ...ref, state: 'broken' };
142
+ }
143
+
144
+ // No anchor — resolve as file
145
+ return {
146
+ ...ref,
147
+ state: 'resolved',
148
+ entityId: `file:${ref.target}`,
149
+ title: ref.target,
150
+ };
151
+ }
152
+
153
+ function resolveIdentity(ref: EntityRef, ctx: ResolverContext): ResolvedRef {
154
+ if (ctx.hasIdentity(ref.target)) {
155
+ return {
156
+ ...ref,
157
+ state: 'resolved',
158
+ entityId: `identity:${ref.target}`,
159
+ title: ref.target,
160
+ };
161
+ }
162
+ return { ...ref, state: 'broken' };
163
+ }
164
+
165
+ function resolveMilestone(ref: EntityRef, ctx: ResolverContext): ResolvedRef {
166
+ const title = ctx.getMilestoneTitle(ref.target);
167
+ if (title !== undefined) {
168
+ return {
169
+ ...ref,
170
+ state: 'resolved',
171
+ entityId: `milestone:${ref.target}`,
172
+ title,
173
+ };
174
+ }
175
+ return { ...ref, state: 'broken' };
176
+ }
177
+
178
+ function resolveDecision(ref: EntityRef, ctx: ResolverContext): ResolvedRef {
179
+ if (ctx.hasDecision(ref.target)) {
180
+ return {
181
+ ...ref,
182
+ state: 'resolved',
183
+ entityId: `decision:${ref.target}`,
184
+ title: ctx.getDecisionTitle(ref.target) ?? ref.target,
185
+ };
186
+ }
187
+ return { ...ref, state: 'broken' };
188
+ }
189
+
190
+ // ---------------------------------------------------------------------------
191
+ // Engine Adapter — creates a ResolverContext from a TrellisVcsEngine
192
+ // ---------------------------------------------------------------------------
193
+
194
+ /**
195
+ * Minimal engine interface for building a ResolverContext.
196
+ * Accepts anything that quacks like TrellisVcsEngine.
197
+ */
198
+ export interface Enginelike {
199
+ trackedFiles(): Array<{ path: string; contentHash: string | undefined }>;
200
+ getIssue(id: string): { title?: string } | null;
201
+ listIssues(filters?: any): Array<{ id: string }>;
202
+ listMilestones(): Array<{ id: string; message?: string }>;
203
+ parseFile(
204
+ content: string,
205
+ filePath: string,
206
+ ): { declarations: Array<{ name: string }> } | null;
207
+ getOps(): Array<{ agentId: string }>;
208
+ getRootPath(): string;
209
+ getDecision?(id: string): { id: string; toolName: string } | null;
210
+ queryDecisions?(filter?: any): Array<{ id: string; toolName: string }>;
211
+ }
212
+
213
+ /**
214
+ * Build a ResolverContext from an engine-like object.
215
+ * The symbol resolution reads files from disk and parses them.
216
+ */
217
+ export function createResolverContext(engine: Enginelike): ResolverContext {
218
+ // Cache tracked files
219
+ const trackedSet = new Set(engine.trackedFiles().map((f) => f.path));
220
+
221
+ // Cache agent IDs from ops
222
+ const agentIds = new Set(engine.getOps().map((op) => op.agentId));
223
+
224
+ // Cache issue IDs
225
+ const issues = engine.listIssues();
226
+
227
+ // Cache milestones
228
+ const milestones = engine.listMilestones();
229
+
230
+ return {
231
+ hasTrackedFile(path: string): boolean {
232
+ return trackedSet.has(path);
233
+ },
234
+
235
+ getIssueTitle(id: string): string | undefined {
236
+ const issue = engine.getIssue(id);
237
+ return issue?.title;
238
+ },
239
+
240
+ getMilestoneTitle(idOrMessage: string): string | undefined {
241
+ // Try exact ID match first
242
+ const byId = milestones.find((m) => m.id === idOrMessage);
243
+ if (byId) return byId.message;
244
+
245
+ // Try matching by message content
246
+ const byMsg = milestones.find(
247
+ (m) =>
248
+ m.message &&
249
+ m.message.toLowerCase().includes(idOrMessage.toLowerCase()),
250
+ );
251
+ return byMsg?.message;
252
+ },
253
+
254
+ hasSymbol(filePath: string, symbolName: string): boolean {
255
+ try {
256
+ const { readFileSync } = require('fs');
257
+ const { join } = require('path');
258
+ const absPath = join(engine.getRootPath(), filePath);
259
+ const content = readFileSync(absPath, 'utf-8');
260
+ const result = engine.parseFile(content, filePath);
261
+ if (!result) return false;
262
+ return result.declarations.some((d) => d.name === symbolName);
263
+ } catch {
264
+ return false;
265
+ }
266
+ },
267
+
268
+ hasIdentity(id: string): boolean {
269
+ // Check if this agent ID appears in any ops
270
+ return agentIds.has(id) || agentIds.has(`agent:${id}`);
271
+ },
272
+
273
+ getKnownAgentIds(): string[] {
274
+ return [...agentIds];
275
+ },
276
+
277
+ getTrackedFilePaths(): string[] {
278
+ return [...trackedSet];
279
+ },
280
+
281
+ getIssueIds(): string[] {
282
+ return issues.map((i) => i.id);
283
+ },
284
+
285
+ getMilestoneIds(): string[] {
286
+ return milestones.map((m) => m.id);
287
+ },
288
+
289
+ hasDecision(id: string): boolean {
290
+ if (!engine.getDecision) return false;
291
+ return engine.getDecision(id) !== null;
292
+ },
293
+
294
+ getDecisionTitle(id: string): string | undefined {
295
+ if (!engine.getDecision) return undefined;
296
+ const d = engine.getDecision(id);
297
+ return d?.toolName;
298
+ },
299
+
300
+ getSymbolNames(filePath: string): string[] {
301
+ try {
302
+ const { readFileSync } = require('fs');
303
+ const { join } = require('path');
304
+ const absPath = join(engine.getRootPath(), filePath);
305
+ const content = readFileSync(absPath, 'utf-8');
306
+ const result = engine.parseFile(content, filePath);
307
+ if (!result) return [];
308
+ return result.declarations.map((d) => d.name);
309
+ } catch {
310
+ return [];
311
+ }
312
+ },
313
+ };
314
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Linked Markdown — Type Definitions
3
+ *
4
+ * Types for wiki-link parsing, entity reference resolution,
5
+ * and bidirectional reference indexing.
6
+ *
7
+ * @see TRL-11
8
+ */
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Reference Namespaces
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export type RefNamespace =
15
+ | 'issue'
16
+ | 'file'
17
+ | 'symbol'
18
+ | 'identity'
19
+ | 'milestone'
20
+ | 'decision';
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Entity Reference (parsed from [[...]] syntax)
24
+ // ---------------------------------------------------------------------------
25
+
26
+ export interface EntityRef {
27
+ /** Full matched text inside [[ ]], e.g. "issue:TRL-5|the parser ticket" */
28
+ raw: string;
29
+ /** Resolved or inferred namespace */
30
+ namespace: RefNamespace;
31
+ /** Target identifier, e.g. "TRL-5", "src/engine.ts" */
32
+ target: string;
33
+ /** Optional anchor for symbol refs, e.g. "createIssue" in [[src/engine.ts#createIssue]] */
34
+ anchor?: string;
35
+ /** Optional display alias, e.g. "the parser ticket" in [[TRL-5|the parser ticket]] */
36
+ alias?: string;
37
+ /** Where this reference was found */
38
+ source: RefSource;
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Reference Source Location
43
+ // ---------------------------------------------------------------------------
44
+
45
+ export interface RefSource {
46
+ /** File containing the reference */
47
+ filePath: string;
48
+ /** 1-indexed line number */
49
+ line: number;
50
+ /** 0-indexed column offset of the opening [[ */
51
+ col: number;
52
+ /** Context in which the ref was found */
53
+ context: RefContext;
54
+ }
55
+
56
+ export type RefContext = 'markdown' | 'jsdoc' | 'pydoc' | 'rustdoc' | 'godoc' | 'comment';
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Resolved Reference
60
+ // ---------------------------------------------------------------------------
61
+
62
+ export type RefState = 'resolved' | 'stale' | 'broken';
63
+
64
+ export interface ResolvedRef extends EntityRef {
65
+ /** Whether the reference target was found */
66
+ state: RefState;
67
+ /** EAV entity ID if resolved (e.g. "issue:TRL-5", "file:src/engine.ts") */
68
+ entityId?: string;
69
+ /** Human-readable label from the resolved entity */
70
+ title?: string;
71
+ /** For stale refs: the op hash that caused the ref to become stale */
72
+ staleOpHash?: string;
73
+ /** For stale refs: reason it became stale */
74
+ staleReason?: 'renamed' | 'deleted';
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Reference Index
79
+ // ---------------------------------------------------------------------------
80
+
81
+ export interface RefIndex {
82
+ /** Forward: source file → refs it contains */
83
+ outgoing: Map<string, EntityRef[]>;
84
+ /** Backward: target entity ID → sources that reference it */
85
+ incoming: Map<string, RefSource[]>;
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // Ref Update Proposal (for rename prompts)
90
+ // ---------------------------------------------------------------------------
91
+
92
+ export interface RefUpdateProposal {
93
+ /** The rename that triggered this proposal */
94
+ oldTarget: string;
95
+ newTarget: string;
96
+ /** Files that would be modified */
97
+ affectedFiles: string[];
98
+ /** Individual ref rewrites */
99
+ rewrites: RefRewrite[];
100
+ }
101
+
102
+ export interface RefRewrite {
103
+ filePath: string;
104
+ line: number;
105
+ col: number;
106
+ oldText: string;
107
+ newText: string;
108
+ }
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * TrellisVCS MCP Server — stdio entry point
4
+ *
5
+ * Run with: bun run src/mcp/index.ts
6
+ * Or via MCP config: { "command": "bun", "args": ["run", "src/mcp/index.ts"] }
7
+ */
8
+
9
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
+ import { createTrellisMcpServer } from './server.js';
11
+
12
+ async function main() {
13
+ const server = createTrellisMcpServer();
14
+ const transport = new StdioServerTransport();
15
+ await server.connect(transport);
16
+ console.error('TrellisVCS MCP Server running on stdio');
17
+ }
18
+
19
+ main().catch((error) => {
20
+ console.error('Fatal error:', error);
21
+ process.exit(1);
22
+ });