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
package/src/vcs/types.ts DELETED
@@ -1,213 +0,0 @@
1
- /**
2
- * TrellisVCS Type Definitions
3
- *
4
- * VCS-specific operation kinds, payloads, and entity types
5
- * that extend the trellis-core kernel primitives.
6
- */
7
-
8
- import type { KernelOp } from '../core/persist/backend.js';
9
-
10
- // ---------------------------------------------------------------------------
11
- // VCS Operation Kinds
12
- // ---------------------------------------------------------------------------
13
-
14
- export type VcsOpKind =
15
- // Tier 0: File-level operations
16
- | 'vcs:fileAdd'
17
- | 'vcs:fileModify'
18
- | 'vcs:fileDelete'
19
- | 'vcs:fileRename'
20
- // Tier 1: Structural operations
21
- | 'vcs:dirAdd'
22
- | 'vcs:dirDelete'
23
- // VCS control operations
24
- | 'vcs:branchCreate'
25
- | 'vcs:branchDelete'
26
- | 'vcs:branchAdvance'
27
- | 'vcs:milestoneCreate'
28
- | 'vcs:checkpointCreate'
29
- | 'vcs:merge'
30
- // Tier 2: AST-level semantic patches (future)
31
- | 'vcs:symbolRename'
32
- | 'vcs:symbolMove'
33
- | 'vcs:symbolExtract'
34
- | 'vcs:signatureChange'
35
- // Issue tracking
36
- | 'vcs:issueCreate'
37
- | 'vcs:issueUpdate'
38
- | 'vcs:issueStart'
39
- | 'vcs:issuePause'
40
- | 'vcs:issueResume'
41
- | 'vcs:issueClose'
42
- | 'vcs:issueReopen'
43
- | 'vcs:criterionAdd'
44
- | 'vcs:criterionUpdate'
45
- // Issue blocking
46
- | 'vcs:issueBlock'
47
- | 'vcs:issueUnblock'
48
- // Decision traces
49
- | 'vcs:decisionRecord';
50
-
51
- // ---------------------------------------------------------------------------
52
- // VCS Operation Payload
53
- // ---------------------------------------------------------------------------
54
-
55
- export interface VcsPayload {
56
- // File operations
57
- filePath?: string;
58
- oldFilePath?: string;
59
- contentHash?: string;
60
- oldContentHash?: string;
61
- size?: number;
62
- language?: string;
63
-
64
- // Branch operations
65
- branchName?: string;
66
- targetOpHash?: string;
67
- sourceBranch?: string;
68
- baseBranch?: string;
69
-
70
- // Milestone operations
71
- milestoneId?: string;
72
- message?: string;
73
- fromOpHash?: string;
74
- toOpHash?: string;
75
-
76
- // Checkpoint operations
77
- trigger?: 'green-build' | 'interval' | 'op-count' | 'manual';
78
-
79
- // Signature
80
- signature?: string;
81
- signedBy?: string;
82
-
83
- // Issue tracking
84
- issueId?: string;
85
- issueTitle?: string;
86
- issueStatus?: 'backlog' | 'queue' | 'in_progress' | 'paused' | 'closed';
87
- issuePriority?: 'critical' | 'high' | 'medium' | 'low';
88
- issueLabels?: string[];
89
- parentIssueId?: string;
90
- issueDescription?: string;
91
- issueAssignee?: string;
92
- pauseNote?: string;
93
- blockedByIssueId?: string;
94
-
95
- // Decision traces
96
- decisionId?: string;
97
- decisionContext?: string;
98
- decisionRationale?: string;
99
- decisionAlternatives?: string;
100
- decisionToolName?: string;
101
- decisionToolInput?: string;
102
- decisionToolOutput?: string;
103
-
104
- // Acceptance criteria
105
- criterionId?: string;
106
- criterionDescription?: string;
107
- criterionCommand?: string;
108
- criterionStatus?: 'pending' | 'passed' | 'failed';
109
- criterionOutput?: string;
110
- }
111
-
112
- /**
113
- * A VcsOp mirrors KernelOp but widens `kind` to accept VCS-specific strings.
114
- * We don't extend KernelOp directly because the kernel types `kind` as a
115
- * narrow union; our VCS kinds are a superset.
116
- */
117
- export interface VcsOp {
118
- hash: string;
119
- kind: VcsOpKind | string;
120
- timestamp: string;
121
- agentId: string;
122
- previousHash?: string;
123
- facts?: import('../core/store/eav-store.js').Fact[];
124
- links?: import('../core/store/eav-store.js').Link[];
125
- vcs?: VcsPayload;
126
- }
127
-
128
- // ---------------------------------------------------------------------------
129
- // File Change Events (from watcher)
130
- // ---------------------------------------------------------------------------
131
-
132
- export interface FileChangeEvent {
133
- type: 'add' | 'modify' | 'delete' | 'rename';
134
- path: string;
135
- oldPath?: string;
136
- contentHash?: string;
137
- oldContentHash?: string;
138
- size?: number;
139
- timestamp: string;
140
- }
141
-
142
- // ---------------------------------------------------------------------------
143
- // Entity ID Helpers
144
- // ---------------------------------------------------------------------------
145
-
146
- export function fileEntityId(path: string): string {
147
- return `file:${path}`;
148
- }
149
-
150
- export function dirEntityId(path: string): string {
151
- return `dir:${path}`;
152
- }
153
-
154
- export function branchEntityId(name: string): string {
155
- return `branch:${name}`;
156
- }
157
-
158
- export function milestoneEntityId(hash: string): string {
159
- return `milestone:${hash}`;
160
- }
161
-
162
- export function checkpointEntityId(hash: string): string {
163
- return `checkpoint:${hash}`;
164
- }
165
-
166
- export function issueEntityId(id: string): string {
167
- return id.startsWith('issue:') ? id : `issue:${id}`;
168
- }
169
-
170
- export function criterionEntityId(issueId: string, index: number): string {
171
- const bare = issueId.replace(/^issue:/, '');
172
- return `criterion:${bare}:ac-${index}`;
173
- }
174
-
175
- export function decisionEntityId(id: string): string {
176
- return id.startsWith('decision:') ? id : `decision:${id}`;
177
- }
178
-
179
- // ---------------------------------------------------------------------------
180
- // Repository Config
181
- // ---------------------------------------------------------------------------
182
-
183
- export interface TrellisVcsConfig {
184
- /** Absolute path to the repository root. */
185
- rootPath: string;
186
-
187
- /** Glob patterns to ignore (e.g. ['node_modules', '.git', '*.log']). */
188
- ignorePatterns: string[];
189
-
190
- /** Debounce interval for file watcher in ms. */
191
- debounceMs: number;
192
-
193
- /** Name of the default branch. */
194
- defaultBranch: string;
195
-
196
- /** Path to the .trellis database file. */
197
- dbPath: string;
198
- }
199
-
200
- export const DEFAULT_CONFIG: Omit<TrellisVcsConfig, 'rootPath'> = {
201
- ignorePatterns: [
202
- 'node_modules',
203
- '.git',
204
- '.trellis',
205
- 'dist',
206
- 'build',
207
- '.DS_Store',
208
- '*.log',
209
- ],
210
- debounceMs: 300,
211
- defaultBranch: 'main',
212
- dbPath: '.trellis/trellis.db',
213
- };
@@ -1,81 +0,0 @@
1
- /**
2
- * VCS Middleware
3
- *
4
- * Intercepts VcsOps in the kernel middleware chain and decomposes
5
- * them into primitive EAV store operations before passing to the
6
- * next middleware (or the store).
7
- */
8
-
9
- import type {
10
- KernelMiddleware,
11
- MiddlewareContext,
12
- OpMiddlewareNext,
13
- } from '../core/kernel/middleware.js';
14
- import type { KernelOp } from '../core/persist/backend.js';
15
- import { decompose } from './decompose.js';
16
- import { isVcsOp } from './ops.js';
17
- import type { VcsOp } from './types.js';
18
-
19
- export class VcsMiddleware implements KernelMiddleware {
20
- name = 'vcs';
21
-
22
- async handleOp(
23
- op: KernelOp,
24
- ctx: MiddlewareContext,
25
- next: OpMiddlewareNext,
26
- ): Promise<void> {
27
- if (!isVcsOp(op as any)) {
28
- return next(op, ctx);
29
- }
30
-
31
- const vcsOp = op as unknown as VcsOp;
32
- const decomposed = decompose(vcsOp);
33
-
34
- // Apply the decomposed primitive operations to the store.
35
- // We create synthetic KernelOps for each batch and pass them through.
36
- if (decomposed.deleteFacts.length > 0) {
37
- await next(
38
- {
39
- ...op,
40
- kind: 'deleteFacts' as any,
41
- facts: decomposed.deleteFacts,
42
- links: undefined,
43
- } as KernelOp,
44
- ctx,
45
- );
46
- }
47
- if (decomposed.deleteLinks.length > 0) {
48
- await next(
49
- {
50
- ...op,
51
- kind: 'deleteLinks' as any,
52
- facts: undefined,
53
- links: decomposed.deleteLinks,
54
- } as KernelOp,
55
- ctx,
56
- );
57
- }
58
- if (decomposed.addFacts.length > 0) {
59
- await next(
60
- {
61
- ...op,
62
- kind: 'addFacts' as any,
63
- facts: decomposed.addFacts,
64
- links: undefined,
65
- } as KernelOp,
66
- ctx,
67
- );
68
- }
69
- if (decomposed.addLinks.length > 0) {
70
- await next(
71
- {
72
- ...op,
73
- kind: 'addLinks' as any,
74
- facts: undefined,
75
- links: decomposed.addLinks,
76
- } as KernelOp,
77
- ctx,
78
- );
79
- }
80
- }
81
- }
@@ -1,255 +0,0 @@
1
- /**
2
- * Filesystem Watcher
3
- *
4
- * Monitors a directory tree for changes using Bun's fs.watch.
5
- * Debounces rapid events and filters ignored paths.
6
- * Emits FileChangeEvents to a callback.
7
- */
8
-
9
- import { watch, type FSWatcher } from 'fs';
10
- import { readdir, stat, readFile } from 'fs/promises';
11
- import { join, relative } from 'path';
12
- import type { FileChangeEvent } from '../vcs/types.js';
13
-
14
- export interface FileWatcherConfig {
15
- rootPath: string;
16
- ignorePatterns: string[];
17
- debounceMs: number;
18
- onEvent: (event: FileChangeEvent) => void | Promise<void>;
19
- }
20
-
21
- export interface ScanProgress {
22
- phase: 'discovering' | 'hashing' | 'done';
23
- current: number;
24
- total: number;
25
- message: string;
26
- }
27
-
28
- /**
29
- * Computes SHA-256 content hash for a file.
30
- */
31
- async function hashFile(filePath: string): Promise<string> {
32
- const content = await readFile(filePath);
33
- const hashBuffer = await crypto.subtle.digest('SHA-256', content);
34
- const hashArray = Array.from(new Uint8Array(hashBuffer));
35
- return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
36
- }
37
-
38
- /**
39
- * Checks if a path matches any ignore pattern (simple glob-like matching).
40
- */
41
- function shouldIgnore(relPath: string, patterns: string[]): boolean {
42
- for (const pattern of patterns) {
43
- // Simple matching: exact segment match or extension glob
44
- if (pattern.startsWith('*.')) {
45
- const ext = pattern.slice(1); // e.g. '.log'
46
- if (relPath.endsWith(ext)) return true;
47
- } else if (relPath.includes(pattern)) {
48
- return true;
49
- }
50
- }
51
- return false;
52
- }
53
-
54
- export class FileWatcher {
55
- private config: FileWatcherConfig;
56
- private watchers: FSWatcher[] = [];
57
- private debounceTimers = new Map<string, ReturnType<typeof setTimeout>>();
58
- private knownFiles = new Map<string, string>(); // relPath → contentHash
59
- private running = false;
60
-
61
- constructor(config: FileWatcherConfig) {
62
- this.config = config;
63
- }
64
-
65
- /**
66
- * Scans the directory tree and builds an initial map of all tracked files.
67
- * Returns the list of FileChangeEvents for the initial state (all adds).
68
- */
69
- async scan(opts?: {
70
- onProgress?: (progress: ScanProgress) => void;
71
- }): Promise<FileChangeEvent[]> {
72
- const events: FileChangeEvent[] = [];
73
- opts?.onProgress?.({
74
- phase: 'discovering',
75
- current: 0,
76
- total: 0,
77
- message: 'Discovering existing files…',
78
- });
79
- const entries = await this.walkDir(this.config.rootPath);
80
- opts?.onProgress?.({
81
- phase: 'hashing',
82
- current: 0,
83
- total: entries.length,
84
- message: `Hashing ${entries.length} existing files…`,
85
- });
86
-
87
- for (let i = 0; i < entries.length; i++) {
88
- const absPath = entries[i];
89
- const relPath = relative(this.config.rootPath, absPath);
90
- if (shouldIgnore(relPath, this.config.ignorePatterns)) continue;
91
-
92
- try {
93
- const hash = await hashFile(absPath);
94
- const stats = await stat(absPath);
95
- this.knownFiles.set(relPath, hash);
96
- events.push({
97
- type: 'add',
98
- path: relPath,
99
- contentHash: hash,
100
- size: stats.size,
101
- timestamp: new Date().toISOString(),
102
- });
103
- } catch {
104
- // File may have been deleted between scan and hash
105
- }
106
-
107
- if ((i + 1) % 25 === 0 || i === entries.length - 1) {
108
- opts?.onProgress?.({
109
- phase: 'hashing',
110
- current: i + 1,
111
- total: entries.length,
112
- message: `Hashed ${i + 1}/${entries.length} files`,
113
- });
114
- }
115
- }
116
-
117
- opts?.onProgress?.({
118
- phase: 'done',
119
- current: events.length,
120
- total: events.length,
121
- message: `Discovered ${events.length} trackable files`,
122
- });
123
-
124
- return events;
125
- }
126
-
127
- /**
128
- * Starts watching the directory tree for changes.
129
- */
130
- start(): void {
131
- if (this.running) return;
132
- this.running = true;
133
-
134
- try {
135
- const watcher = watch(
136
- this.config.rootPath,
137
- { recursive: true },
138
- (eventType, filename) => {
139
- if (!filename) return;
140
- const relPath = filename.toString();
141
- if (shouldIgnore(relPath, this.config.ignorePatterns)) return;
142
- this.debouncedHandle(relPath);
143
- },
144
- );
145
- this.watchers.push(watcher);
146
- } catch {
147
- // recursive watch not supported on all platforms; fall back gracefully
148
- console.warn('Recursive watch not supported; using scan-based polling.');
149
- }
150
- }
151
-
152
- /**
153
- * Stops all watchers.
154
- */
155
- stop(): void {
156
- this.running = false;
157
- for (const w of this.watchers) {
158
- w.close();
159
- }
160
- this.watchers = [];
161
- for (const timer of this.debounceTimers.values()) {
162
- clearTimeout(timer);
163
- }
164
- this.debounceTimers.clear();
165
- }
166
-
167
- /**
168
- * Returns the current known file map (path → contentHash).
169
- */
170
- getKnownFiles(): Map<string, string> {
171
- return new Map(this.knownFiles);
172
- }
173
-
174
- private debouncedHandle(relPath: string): void {
175
- const existing = this.debounceTimers.get(relPath);
176
- if (existing) clearTimeout(existing);
177
-
178
- const timer = setTimeout(async () => {
179
- this.debounceTimers.delete(relPath);
180
- await this.handleChange(relPath);
181
- }, this.config.debounceMs);
182
-
183
- this.debounceTimers.set(relPath, timer);
184
- }
185
-
186
- private async handleChange(relPath: string): Promise<void> {
187
- const absPath = join(this.config.rootPath, relPath);
188
- const known = this.knownFiles.get(relPath);
189
-
190
- try {
191
- const stats = await stat(absPath);
192
- if (!stats.isFile()) return;
193
-
194
- const hash = await hashFile(absPath);
195
-
196
- if (!known) {
197
- // New file
198
- this.knownFiles.set(relPath, hash);
199
- await this.config.onEvent({
200
- type: 'add',
201
- path: relPath,
202
- contentHash: hash,
203
- size: stats.size,
204
- timestamp: new Date().toISOString(),
205
- });
206
- } else if (known !== hash) {
207
- // Modified file
208
- const oldHash = known;
209
- this.knownFiles.set(relPath, hash);
210
- await this.config.onEvent({
211
- type: 'modify',
212
- path: relPath,
213
- contentHash: hash,
214
- oldContentHash: oldHash,
215
- size: stats.size,
216
- timestamp: new Date().toISOString(),
217
- });
218
- }
219
- // If hash is the same, no event (unchanged)
220
- } catch {
221
- // File doesn't exist → it was deleted
222
- if (known) {
223
- this.knownFiles.delete(relPath);
224
- await this.config.onEvent({
225
- type: 'delete',
226
- path: relPath,
227
- contentHash: known,
228
- timestamp: new Date().toISOString(),
229
- });
230
- }
231
- }
232
- }
233
-
234
- private async walkDir(dir: string): Promise<string[]> {
235
- const results: string[] = [];
236
- try {
237
- const entries = await readdir(dir, { withFileTypes: true });
238
- for (const entry of entries) {
239
- const fullPath = join(dir, entry.name);
240
- const relFromRoot = relative(this.config.rootPath, fullPath);
241
- if (shouldIgnore(relFromRoot, this.config.ignorePatterns)) continue;
242
-
243
- if (entry.isDirectory()) {
244
- const sub = await this.walkDir(fullPath);
245
- results.push(...sub);
246
- } else if (entry.isFile()) {
247
- results.push(fullPath);
248
- }
249
- }
250
- } catch {
251
- // Permission error or deleted dir
252
- }
253
- return results;
254
- }
255
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * File Watcher
3
- *
4
- * Monitors a directory for filesystem changes and emits FileChangeEvents.
5
- * Uses Bun's built-in fs.watch for zero-dependency file monitoring.
6
- */
7
-
8
- export { FileWatcher } from './fs-watcher.js';
9
- export { Ingestion } from './ingestion.js';
@@ -1,116 +0,0 @@
1
- /**
2
- * Ingestion Pipeline
3
- *
4
- * Bridges the file watcher and the kernel: converts FileChangeEvents
5
- * into VcsOps and applies them to the kernel via mutate().
6
- */
7
-
8
- import type { FileChangeEvent, VcsOpKind } from '../vcs/types.js';
9
- import { createVcsOp } from '../vcs/ops.js';
10
- import type { VcsOp } from '../vcs/types.js';
11
- import { extname } from 'path';
12
-
13
- // Simple language detection from file extension
14
- const EXT_LANGUAGE: Record<string, string> = {
15
- '.ts': 'typescript',
16
- '.tsx': 'typescript',
17
- '.js': 'javascript',
18
- '.jsx': 'javascript',
19
- '.py': 'python',
20
- '.rs': 'rust',
21
- '.go': 'go',
22
- '.rb': 'ruby',
23
- '.java': 'java',
24
- '.c': 'c',
25
- '.cpp': 'cpp',
26
- '.h': 'c',
27
- '.hpp': 'cpp',
28
- '.cs': 'csharp',
29
- '.swift': 'swift',
30
- '.kt': 'kotlin',
31
- '.md': 'markdown',
32
- '.json': 'json',
33
- '.yaml': 'yaml',
34
- '.yml': 'yaml',
35
- '.toml': 'toml',
36
- '.html': 'html',
37
- '.css': 'css',
38
- '.scss': 'scss',
39
- '.vue': 'vue',
40
- '.svelte': 'svelte',
41
- };
42
-
43
- function detectLanguage(filePath: string): string | undefined {
44
- const ext = extname(filePath).toLowerCase();
45
- return EXT_LANGUAGE[ext];
46
- }
47
-
48
- export class Ingestion {
49
- private agentId: string;
50
- private lastOpHash: string | undefined;
51
- private onOp: (op: VcsOp) => void | Promise<void>;
52
-
53
- constructor(opts: {
54
- agentId: string;
55
- lastOpHash?: string;
56
- onOp: (op: VcsOp) => void | Promise<void>;
57
- }) {
58
- this.agentId = opts.agentId;
59
- this.lastOpHash = opts.lastOpHash;
60
- this.onOp = opts.onOp;
61
- }
62
-
63
- /**
64
- * Processes a single FileChangeEvent, producing and emitting a VcsOp.
65
- */
66
- async process(event: FileChangeEvent): Promise<VcsOp> {
67
- let kind: VcsOpKind;
68
-
69
- switch (event.type) {
70
- case 'add':
71
- kind = 'vcs:fileAdd';
72
- break;
73
- case 'modify':
74
- kind = 'vcs:fileModify';
75
- break;
76
- case 'delete':
77
- kind = 'vcs:fileDelete';
78
- break;
79
- case 'rename':
80
- kind = 'vcs:fileRename';
81
- break;
82
- }
83
-
84
- const op = await createVcsOp(kind, {
85
- agentId: this.agentId,
86
- previousHash: this.lastOpHash,
87
- vcs: {
88
- filePath: event.path,
89
- oldFilePath: event.oldPath,
90
- contentHash: event.contentHash,
91
- oldContentHash: event.oldContentHash,
92
- size: event.size,
93
- language: detectLanguage(event.path),
94
- },
95
- });
96
-
97
- this.lastOpHash = op.hash;
98
- await this.onOp(op);
99
- return op;
100
- }
101
-
102
- /**
103
- * Processes a batch of FileChangeEvents in order.
104
- */
105
- async processBatch(events: FileChangeEvent[]): Promise<VcsOp[]> {
106
- const ops: VcsOp[] = [];
107
- for (const event of events) {
108
- ops.push(await this.process(event));
109
- }
110
- return ops;
111
- }
112
-
113
- getLastOpHash(): string | undefined {
114
- return this.lastOpHash;
115
- }
116
- }