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.
- package/dist/cli/index.js +1 -1
- package/dist/embeddings/index.js +1 -1
- package/dist/{index-7gvjxt27.js → index-2917tjd8.js} +1 -1
- package/package.json +2 -10
- package/dist/transformers.node-bx3q9d7k.js +0 -33130
- package/src/cli/index.ts +0 -3356
- package/src/core/agents/harness.ts +0 -380
- package/src/core/agents/index.ts +0 -18
- package/src/core/agents/types.ts +0 -90
- package/src/core/index.ts +0 -118
- package/src/core/kernel/middleware.ts +0 -44
- package/src/core/kernel/trellis-kernel.ts +0 -593
- package/src/core/ontology/builtins.ts +0 -248
- package/src/core/ontology/index.ts +0 -34
- package/src/core/ontology/registry.ts +0 -209
- package/src/core/ontology/types.ts +0 -124
- package/src/core/ontology/validator.ts +0 -382
- package/src/core/persist/backend.ts +0 -74
- package/src/core/persist/sqlite-backend.ts +0 -298
- package/src/core/plugins/index.ts +0 -17
- package/src/core/plugins/registry.ts +0 -322
- package/src/core/plugins/types.ts +0 -126
- package/src/core/query/datalog.ts +0 -188
- package/src/core/query/engine.ts +0 -370
- package/src/core/query/index.ts +0 -34
- package/src/core/query/parser.ts +0 -481
- package/src/core/query/types.ts +0 -200
- package/src/core/store/eav-store.ts +0 -467
- package/src/decisions/auto-capture.ts +0 -136
- package/src/decisions/hooks.ts +0 -163
- package/src/decisions/index.ts +0 -261
- package/src/decisions/types.ts +0 -103
- package/src/embeddings/auto-embed.ts +0 -248
- package/src/embeddings/chunker.ts +0 -327
- package/src/embeddings/index.ts +0 -48
- package/src/embeddings/model.ts +0 -112
- package/src/embeddings/search.ts +0 -305
- package/src/embeddings/store.ts +0 -313
- package/src/embeddings/types.ts +0 -92
- package/src/engine.ts +0 -1125
- package/src/garden/cluster.ts +0 -330
- package/src/garden/garden.ts +0 -306
- package/src/garden/index.ts +0 -29
- package/src/git/git-exporter.ts +0 -286
- package/src/git/git-importer.ts +0 -329
- package/src/git/git-reader.ts +0 -189
- package/src/git/index.ts +0 -22
- package/src/identity/governance.ts +0 -211
- package/src/identity/identity.ts +0 -224
- package/src/identity/index.ts +0 -30
- package/src/identity/signing-middleware.ts +0 -97
- package/src/index.ts +0 -29
- package/src/links/index.ts +0 -49
- package/src/links/lifecycle.ts +0 -400
- package/src/links/parser.ts +0 -484
- package/src/links/ref-index.ts +0 -186
- package/src/links/resolver.ts +0 -314
- package/src/links/types.ts +0 -108
- package/src/mcp/index.ts +0 -22
- package/src/mcp/server.ts +0 -1278
- package/src/semantic/csharp-parser.ts +0 -493
- package/src/semantic/go-parser.ts +0 -585
- package/src/semantic/index.ts +0 -34
- package/src/semantic/java-parser.ts +0 -456
- package/src/semantic/python-parser.ts +0 -659
- package/src/semantic/ruby-parser.ts +0 -446
- package/src/semantic/rust-parser.ts +0 -784
- package/src/semantic/semantic-merge.ts +0 -210
- package/src/semantic/ts-parser.ts +0 -681
- package/src/semantic/types.ts +0 -175
- package/src/sync/http-transport.ts +0 -144
- package/src/sync/index.ts +0 -43
- package/src/sync/memory-transport.ts +0 -66
- package/src/sync/multi-repo.ts +0 -200
- package/src/sync/reconciler.ts +0 -237
- package/src/sync/sync-engine.ts +0 -258
- package/src/sync/types.ts +0 -104
- package/src/sync/ws-transport.ts +0 -145
- package/src/ui/client.html +0 -695
- package/src/ui/server.ts +0 -419
- package/src/vcs/blob-store.ts +0 -124
- package/src/vcs/branch.ts +0 -150
- package/src/vcs/checkpoint.ts +0 -64
- package/src/vcs/decompose.ts +0 -469
- package/src/vcs/diff.ts +0 -409
- package/src/vcs/engine-context.ts +0 -26
- package/src/vcs/index.ts +0 -23
- package/src/vcs/issue.ts +0 -800
- package/src/vcs/merge.ts +0 -425
- package/src/vcs/milestone.ts +0 -124
- package/src/vcs/ops.ts +0 -59
- package/src/vcs/types.ts +0 -213
- package/src/vcs/vcs-middleware.ts +0 -81
- package/src/watcher/fs-watcher.ts +0 -255
- package/src/watcher/index.ts +0 -9
- package/src/watcher/ingestion.ts +0 -116
package/src/git/git-exporter.ts
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git Exporter
|
|
3
|
-
*
|
|
4
|
-
* Serializes TrellisVCS milestones back to Git commits.
|
|
5
|
-
* Each milestone becomes a Git commit with:
|
|
6
|
-
* - The milestone message as the commit message
|
|
7
|
-
* - File states reconstructed from the blob store
|
|
8
|
-
* - Author info from the milestone's createdBy identity
|
|
9
|
-
*
|
|
10
|
-
* The exporter creates a new Git repo (or uses an existing one)
|
|
11
|
-
* and leaves the TrellisVCS repo untouched.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { execSync } from 'child_process';
|
|
15
|
-
import { existsSync, mkdirSync, writeFileSync, unlinkSync } from 'fs';
|
|
16
|
-
import { join, dirname } from 'path';
|
|
17
|
-
import { TrellisVcsEngine } from '../engine.js';
|
|
18
|
-
import { BlobStore } from '../vcs/blob-store.js';
|
|
19
|
-
import type { VcsOp } from '../vcs/types.js';
|
|
20
|
-
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// Types
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
export interface ExportOptions {
|
|
26
|
-
/** Path to the TrellisVCS repository to export from. */
|
|
27
|
-
from: string;
|
|
28
|
-
|
|
29
|
-
/** Path to the target Git repository. */
|
|
30
|
-
to: string;
|
|
31
|
-
|
|
32
|
-
/** Author name for commits (default: "TrellisVCS Export"). */
|
|
33
|
-
authorName?: string;
|
|
34
|
-
|
|
35
|
-
/** Author email for commits (default: "export@trellis.dev"). */
|
|
36
|
-
authorEmail?: string;
|
|
37
|
-
|
|
38
|
-
/** Callback for progress reporting. */
|
|
39
|
-
onProgress?: (progress: ExportProgress) => void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface ExportProgress {
|
|
43
|
-
phase: 'preparing' | 'exporting' | 'done';
|
|
44
|
-
current: number;
|
|
45
|
-
total: number;
|
|
46
|
-
message: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ExportResult {
|
|
50
|
-
milestonesExported: number;
|
|
51
|
-
commitsCreated: number;
|
|
52
|
-
duration: number;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
// Exporter
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
|
|
59
|
-
export async function exportToGit(opts: ExportOptions): Promise<ExportResult> {
|
|
60
|
-
const startTime = Date.now();
|
|
61
|
-
|
|
62
|
-
// Open the TrellisVCS repo
|
|
63
|
-
const engine = new TrellisVcsEngine({ rootPath: opts.from });
|
|
64
|
-
engine.open();
|
|
65
|
-
|
|
66
|
-
const blobStore = engine.getBlobStore();
|
|
67
|
-
if (!blobStore) {
|
|
68
|
-
throw new Error('Blob store not available. Re-open the repo first.');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Get all milestones and ops
|
|
72
|
-
const milestones = engine.listMilestones();
|
|
73
|
-
const allOps = engine.getOps();
|
|
74
|
-
|
|
75
|
-
opts.onProgress?.({
|
|
76
|
-
phase: 'preparing',
|
|
77
|
-
current: 0,
|
|
78
|
-
total: milestones.length,
|
|
79
|
-
message: `Found ${milestones.length} milestones to export`,
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
if (milestones.length === 0) {
|
|
83
|
-
return {
|
|
84
|
-
milestonesExported: 0,
|
|
85
|
-
commitsCreated: 0,
|
|
86
|
-
duration: Date.now() - startTime,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Initialize target Git repo
|
|
91
|
-
if (!existsSync(opts.to)) {
|
|
92
|
-
mkdirSync(opts.to, { recursive: true });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const isGitRepo = existsSync(join(opts.to, '.git'));
|
|
96
|
-
if (!isGitRepo) {
|
|
97
|
-
git(opts.to, 'init');
|
|
98
|
-
git(
|
|
99
|
-
opts.to,
|
|
100
|
-
`config user.email "${opts.authorEmail ?? 'export@trellis.dev'}"`,
|
|
101
|
-
);
|
|
102
|
-
git(
|
|
103
|
-
opts.to,
|
|
104
|
-
`config user.name "${opts.authorName ?? 'TrellisVCS Export'}"`,
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
let commitsCreated = 0;
|
|
109
|
-
|
|
110
|
-
// Build a milestone-ordered export by walking the op stream sequentially.
|
|
111
|
-
// Each milestone op marks a commit boundary. Ops between milestones are
|
|
112
|
-
// applied to the working directory, then committed with the milestone's
|
|
113
|
-
// message. This works regardless of whether milestones have
|
|
114
|
-
// fromOpHash/toOpHash (imported milestones often don't).
|
|
115
|
-
|
|
116
|
-
// Build milestone lookup: milestoneId → milestone metadata
|
|
117
|
-
const milestoneMap = new Map(milestones.map((m) => [m.id, m]));
|
|
118
|
-
|
|
119
|
-
// Cumulative file state tracker
|
|
120
|
-
const fileStates = new Map<string, FileState>();
|
|
121
|
-
let pendingChanges = false;
|
|
122
|
-
let milestoneIdx = 0;
|
|
123
|
-
|
|
124
|
-
for (const op of allOps) {
|
|
125
|
-
// Track file changes
|
|
126
|
-
if (op.vcs?.filePath) {
|
|
127
|
-
switch (op.kind) {
|
|
128
|
-
case 'vcs:fileAdd':
|
|
129
|
-
case 'vcs:fileModify':
|
|
130
|
-
fileStates.set(op.vcs.filePath, { contentHash: op.vcs.contentHash });
|
|
131
|
-
pendingChanges = true;
|
|
132
|
-
break;
|
|
133
|
-
case 'vcs:fileDelete':
|
|
134
|
-
fileStates.set(op.vcs.filePath, { deleted: true });
|
|
135
|
-
pendingChanges = true;
|
|
136
|
-
break;
|
|
137
|
-
case 'vcs:fileRename':
|
|
138
|
-
if (op.vcs.oldFilePath) {
|
|
139
|
-
fileStates.set(op.vcs.oldFilePath, { deleted: true });
|
|
140
|
-
}
|
|
141
|
-
fileStates.set(op.vcs.filePath, { contentHash: op.vcs.contentHash });
|
|
142
|
-
pendingChanges = true;
|
|
143
|
-
break;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Check if this op is a milestone boundary
|
|
148
|
-
if (op.kind !== 'vcs:milestoneCreate') continue;
|
|
149
|
-
|
|
150
|
-
const milestoneId = op.vcs?.milestoneId;
|
|
151
|
-
const milestone = milestoneId ? milestoneMap.get(milestoneId) : undefined;
|
|
152
|
-
milestoneIdx++;
|
|
153
|
-
|
|
154
|
-
opts.onProgress?.({
|
|
155
|
-
phase: 'exporting',
|
|
156
|
-
current: milestoneIdx,
|
|
157
|
-
total: milestones.length,
|
|
158
|
-
message: `Exporting milestone ${milestoneIdx}/${milestones.length}: ${(op.vcs?.message ?? '').slice(0, 60)}`,
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// Write current file state snapshot to the git working directory
|
|
162
|
-
for (const [filePath, state] of fileStates.entries()) {
|
|
163
|
-
const absPath = join(opts.to, filePath);
|
|
164
|
-
|
|
165
|
-
if (state.deleted) {
|
|
166
|
-
if (existsSync(absPath)) {
|
|
167
|
-
unlinkSync(absPath);
|
|
168
|
-
}
|
|
169
|
-
} else if (state.contentHash && blobStore) {
|
|
170
|
-
const content = blobStore.get(state.contentHash);
|
|
171
|
-
if (content) {
|
|
172
|
-
const dir = dirname(absPath);
|
|
173
|
-
if (!existsSync(dir)) {
|
|
174
|
-
mkdirSync(dir, { recursive: true });
|
|
175
|
-
}
|
|
176
|
-
writeFileSync(absPath, content);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Stage all changes
|
|
182
|
-
git(opts.to, 'add -A');
|
|
183
|
-
|
|
184
|
-
// Check if there are changes to commit
|
|
185
|
-
const status = git(opts.to, 'status --porcelain');
|
|
186
|
-
if (status.trim().length === 0 && commitsCreated > 0) {
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Extract author info
|
|
191
|
-
const authorName =
|
|
192
|
-
opts.authorName ?? extractAuthorName(milestone?.createdBy ?? op.agentId);
|
|
193
|
-
const authorEmail =
|
|
194
|
-
opts.authorEmail ??
|
|
195
|
-
extractAuthorEmail(milestone?.createdBy ?? op.agentId);
|
|
196
|
-
const message =
|
|
197
|
-
op.vcs?.message ?? milestone?.message ?? `Milestone ${milestoneId}`;
|
|
198
|
-
const date =
|
|
199
|
-
milestone?.createdAt ?? op.timestamp ?? new Date().toISOString();
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
gitWithEnv(
|
|
203
|
-
opts.to,
|
|
204
|
-
`commit --allow-empty --author="${authorName} <${authorEmail}>" -m "${escapeMessage(message)}"`,
|
|
205
|
-
{
|
|
206
|
-
GIT_AUTHOR_DATE: date,
|
|
207
|
-
GIT_COMMITTER_DATE: date,
|
|
208
|
-
},
|
|
209
|
-
);
|
|
210
|
-
commitsCreated++;
|
|
211
|
-
pendingChanges = false;
|
|
212
|
-
} catch {
|
|
213
|
-
// If commit fails, skip
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
opts.onProgress?.({
|
|
218
|
-
phase: 'done',
|
|
219
|
-
current: milestones.length,
|
|
220
|
-
total: milestones.length,
|
|
221
|
-
message: `Exported ${commitsCreated} commits from ${milestones.length} milestones`,
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
milestonesExported: milestones.length,
|
|
226
|
-
commitsCreated,
|
|
227
|
-
duration: Date.now() - startTime,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// ---------------------------------------------------------------------------
|
|
232
|
-
// Helpers
|
|
233
|
-
// ---------------------------------------------------------------------------
|
|
234
|
-
|
|
235
|
-
function git(repoPath: string, command: string): string {
|
|
236
|
-
try {
|
|
237
|
-
return execSync(`git -C "${repoPath}" ${command}`, {
|
|
238
|
-
encoding: 'utf-8',
|
|
239
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
240
|
-
});
|
|
241
|
-
} catch (err: any) {
|
|
242
|
-
if (err.stdout) return err.stdout;
|
|
243
|
-
throw err;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function gitWithEnv(
|
|
248
|
-
repoPath: string,
|
|
249
|
-
command: string,
|
|
250
|
-
extraEnv: Record<string, string>,
|
|
251
|
-
): string {
|
|
252
|
-
return execSync(`git -C "${repoPath}" ${command}`, {
|
|
253
|
-
encoding: 'utf-8',
|
|
254
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
255
|
-
env: { ...process.env, ...extraEnv },
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function escapeMessage(msg: string): string {
|
|
260
|
-
return msg.replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function extractAuthorName(createdBy?: string): string {
|
|
264
|
-
if (!createdBy) return 'TrellisVCS Export';
|
|
265
|
-
// createdBy is typically "identity:alice@example.com" or "identity:alice"
|
|
266
|
-
const id = createdBy.replace('identity:', '');
|
|
267
|
-
// If it looks like an email, extract the name part
|
|
268
|
-
if (id.includes('@')) {
|
|
269
|
-
return id.split('@')[0];
|
|
270
|
-
}
|
|
271
|
-
return id;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function extractAuthorEmail(createdBy?: string): string {
|
|
275
|
-
if (!createdBy) return 'export@trellis.dev';
|
|
276
|
-
const id = createdBy.replace('identity:', '');
|
|
277
|
-
if (id.includes('@')) {
|
|
278
|
-
return id;
|
|
279
|
-
}
|
|
280
|
-
return `${id}@trellis.dev`;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
interface FileState {
|
|
284
|
-
contentHash?: string;
|
|
285
|
-
deleted?: boolean;
|
|
286
|
-
}
|
package/src/git/git-importer.ts
DELETED
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git Importer
|
|
3
|
-
*
|
|
4
|
-
* Converts a Git repository's commit history into TrellisVCS operations.
|
|
5
|
-
* Each Git commit becomes:
|
|
6
|
-
* 1. A sequence of Tier 0 file-level ops (vcs:fileAdd, vcs:fileModify, etc.)
|
|
7
|
-
* 2. A vcs:milestoneCreate op (commit message → milestone)
|
|
8
|
-
*
|
|
9
|
-
* The importer reads from the Git repo, writes into a TrellisVCS engine,
|
|
10
|
-
* and leaves the original Git repo untouched.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
GitReader,
|
|
15
|
-
type GitCommitWithChanges,
|
|
16
|
-
type GitFileChange,
|
|
17
|
-
} from './git-reader.js';
|
|
18
|
-
import { TrellisVcsEngine } from '../engine.js';
|
|
19
|
-
import { createVcsOp } from '../vcs/ops.js';
|
|
20
|
-
import type { VcsOp, VcsOpKind } from '../vcs/types.js';
|
|
21
|
-
import { existsSync, mkdirSync, readFileSync } from 'fs';
|
|
22
|
-
import { join, dirname } from 'path';
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Types
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
export interface ImportOptions {
|
|
29
|
-
/** Path to the source Git repository. */
|
|
30
|
-
from: string;
|
|
31
|
-
|
|
32
|
-
/** Path to the target TrellisVCS repository (defaults to cwd). */
|
|
33
|
-
to: string;
|
|
34
|
-
|
|
35
|
-
/** Agent ID for the import. Defaults to Git author info. */
|
|
36
|
-
agentId?: string;
|
|
37
|
-
|
|
38
|
-
/** Callback for progress reporting. */
|
|
39
|
-
onProgress?: (progress: ImportProgress) => void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface ImportProgress {
|
|
43
|
-
phase: 'reading' | 'importing' | 'done';
|
|
44
|
-
current: number;
|
|
45
|
-
total: number;
|
|
46
|
-
message: string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface ImportResult {
|
|
50
|
-
commitsImported: number;
|
|
51
|
-
opsCreated: number;
|
|
52
|
-
filesTracked: number;
|
|
53
|
-
branches: string[];
|
|
54
|
-
duration: number;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Importer
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
export async function importFromGit(
|
|
62
|
-
opts: ImportOptions,
|
|
63
|
-
): Promise<ImportResult> {
|
|
64
|
-
const startTime = Date.now();
|
|
65
|
-
const gitReader = new GitReader(opts.from);
|
|
66
|
-
|
|
67
|
-
if (!gitReader.isGitRepo()) {
|
|
68
|
-
throw new Error(`Not a Git repository: ${opts.from}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// --- Phase 1: Read Git history ---
|
|
72
|
-
opts.onProgress?.({
|
|
73
|
-
phase: 'reading',
|
|
74
|
-
current: 0,
|
|
75
|
-
total: 0,
|
|
76
|
-
message: 'Reading Git history…',
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const history = gitReader.readFullHistory();
|
|
80
|
-
const branches = gitReader.branches();
|
|
81
|
-
const defaultBranch = gitReader.currentBranch();
|
|
82
|
-
|
|
83
|
-
opts.onProgress?.({
|
|
84
|
-
phase: 'reading',
|
|
85
|
-
current: history.length,
|
|
86
|
-
total: history.length,
|
|
87
|
-
message: `Read ${history.length} commits`,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// --- Phase 2: Create TrellisVCS repo and import ---
|
|
91
|
-
const engine = new TrellisVcsEngine({
|
|
92
|
-
rootPath: opts.to,
|
|
93
|
-
agentId: opts.agentId ?? `git-import:${opts.from}`,
|
|
94
|
-
defaultBranch,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Initialize the target repo (creates .trellis/ but skip file scan)
|
|
98
|
-
const trellisDir = join(opts.to, '.trellis');
|
|
99
|
-
if (!existsSync(trellisDir)) {
|
|
100
|
-
mkdirSync(trellisDir, { recursive: true });
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// We need to manually initialize since we don't want the default file scan.
|
|
104
|
-
// Create branch op + import commits.
|
|
105
|
-
const importEngine = new ImportEngine(engine, opts);
|
|
106
|
-
|
|
107
|
-
await importEngine.createBranch(defaultBranch);
|
|
108
|
-
|
|
109
|
-
let opsCreated = 1; // branch op
|
|
110
|
-
const trackedFiles = new Set<string>();
|
|
111
|
-
|
|
112
|
-
for (let i = 0; i < history.length; i++) {
|
|
113
|
-
const commit = history[i];
|
|
114
|
-
|
|
115
|
-
opts.onProgress?.({
|
|
116
|
-
phase: 'importing',
|
|
117
|
-
current: i + 1,
|
|
118
|
-
total: history.length,
|
|
119
|
-
message: `Importing commit ${i + 1}/${history.length}: ${commit.message.slice(0, 60)}`,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Convert each file change to a VcsOp
|
|
123
|
-
for (const change of commit.changes) {
|
|
124
|
-
const op = await importEngine.convertChange(change, commit);
|
|
125
|
-
opsCreated++;
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
change.status === 'A' ||
|
|
129
|
-
change.status === 'M' ||
|
|
130
|
-
change.status === 'R'
|
|
131
|
-
) {
|
|
132
|
-
trackedFiles.add(change.path);
|
|
133
|
-
}
|
|
134
|
-
if (change.status === 'D') {
|
|
135
|
-
trackedFiles.delete(change.path);
|
|
136
|
-
}
|
|
137
|
-
if (change.status === 'R' && change.oldPath) {
|
|
138
|
-
trackedFiles.delete(change.oldPath);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Create a milestone for this commit
|
|
143
|
-
await importEngine.createMilestone(commit);
|
|
144
|
-
opsCreated++;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
opts.onProgress?.({
|
|
148
|
-
phase: 'done',
|
|
149
|
-
current: history.length,
|
|
150
|
-
total: history.length,
|
|
151
|
-
message: `Imported ${history.length} commits → ${opsCreated} ops`,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
commitsImported: history.length,
|
|
156
|
-
opsCreated,
|
|
157
|
-
filesTracked: trackedFiles.size,
|
|
158
|
-
branches,
|
|
159
|
-
duration: Date.now() - startTime,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// ---------------------------------------------------------------------------
|
|
164
|
-
// Internal Import Engine
|
|
165
|
-
// ---------------------------------------------------------------------------
|
|
166
|
-
|
|
167
|
-
class ImportEngine {
|
|
168
|
-
private engine: TrellisVcsEngine;
|
|
169
|
-
private opts: ImportOptions;
|
|
170
|
-
private lastOpHash: string | undefined;
|
|
171
|
-
private ops: VcsOp[] = [];
|
|
172
|
-
|
|
173
|
-
constructor(engine: TrellisVcsEngine, opts: ImportOptions) {
|
|
174
|
-
this.engine = engine;
|
|
175
|
-
this.opts = opts;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async createBranch(name: string): Promise<void> {
|
|
179
|
-
const op = await createVcsOp('vcs:branchCreate', {
|
|
180
|
-
agentId: this.agentId(),
|
|
181
|
-
previousHash: this.lastOpHash,
|
|
182
|
-
vcs: { branchName: name },
|
|
183
|
-
});
|
|
184
|
-
this.append(op);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async convertChange(
|
|
188
|
-
change: GitFileChange,
|
|
189
|
-
commit: GitCommitWithChanges,
|
|
190
|
-
): Promise<VcsOp> {
|
|
191
|
-
const agentId = `identity:${commit.authorEmail}`;
|
|
192
|
-
let kind: VcsOpKind;
|
|
193
|
-
|
|
194
|
-
switch (change.status) {
|
|
195
|
-
case 'A':
|
|
196
|
-
kind = 'vcs:fileAdd';
|
|
197
|
-
break;
|
|
198
|
-
case 'M':
|
|
199
|
-
kind = 'vcs:fileModify';
|
|
200
|
-
break;
|
|
201
|
-
case 'D':
|
|
202
|
-
kind = 'vcs:fileDelete';
|
|
203
|
-
break;
|
|
204
|
-
case 'R':
|
|
205
|
-
kind = 'vcs:fileRename';
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Hash the file content at this commit for content-addressability
|
|
210
|
-
let contentHash: string | undefined;
|
|
211
|
-
if (change.status !== 'D') {
|
|
212
|
-
contentHash = await this.hashFileAtCommit(commit.hash, change.path);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
let oldContentHash: string | undefined;
|
|
216
|
-
if (change.status === 'M' && commit.parentHashes[0]) {
|
|
217
|
-
oldContentHash = await this.hashFileAtCommit(
|
|
218
|
-
commit.parentHashes[0],
|
|
219
|
-
change.path,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const op = await createVcsOp(kind, {
|
|
224
|
-
agentId,
|
|
225
|
-
previousHash: this.lastOpHash,
|
|
226
|
-
vcs: {
|
|
227
|
-
filePath: change.path,
|
|
228
|
-
oldFilePath: change.oldPath,
|
|
229
|
-
contentHash,
|
|
230
|
-
oldContentHash,
|
|
231
|
-
},
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// Override timestamp to match Git commit time
|
|
235
|
-
(op as any).timestamp = commit.timestamp;
|
|
236
|
-
|
|
237
|
-
this.append(op);
|
|
238
|
-
return op;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async createMilestone(commit: GitCommitWithChanges): Promise<void> {
|
|
242
|
-
const agentId = `identity:${commit.authorEmail}`;
|
|
243
|
-
const milestoneId = `milestone:git:${commit.hash.slice(0, 12)}`;
|
|
244
|
-
|
|
245
|
-
const op = await createVcsOp('vcs:milestoneCreate', {
|
|
246
|
-
agentId,
|
|
247
|
-
previousHash: this.lastOpHash,
|
|
248
|
-
vcs: {
|
|
249
|
-
milestoneId,
|
|
250
|
-
message: commit.message,
|
|
251
|
-
},
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// Override timestamp to match Git commit time
|
|
255
|
-
(op as any).timestamp = commit.timestamp;
|
|
256
|
-
|
|
257
|
-
this.append(op);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
private append(op: VcsOp): void {
|
|
261
|
-
this.lastOpHash = op.hash;
|
|
262
|
-
this.ops.push(op);
|
|
263
|
-
|
|
264
|
-
// Write ops to the engine's op log
|
|
265
|
-
this.flushOps();
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private flushOps(): void {
|
|
269
|
-
const opsPath = join(this.opts.to, '.trellis', 'ops.json');
|
|
270
|
-
const configPath = join(this.opts.to, '.trellis', 'config.json');
|
|
271
|
-
const trellisDir = join(this.opts.to, '.trellis');
|
|
272
|
-
|
|
273
|
-
if (!existsSync(trellisDir)) {
|
|
274
|
-
mkdirSync(trellisDir, { recursive: true });
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Write config if it doesn't exist
|
|
278
|
-
if (!existsSync(configPath)) {
|
|
279
|
-
const config = {
|
|
280
|
-
rootPath: this.opts.to,
|
|
281
|
-
ignorePatterns: [
|
|
282
|
-
'node_modules',
|
|
283
|
-
'.git',
|
|
284
|
-
'.trellis',
|
|
285
|
-
'dist',
|
|
286
|
-
'build',
|
|
287
|
-
'.DS_Store',
|
|
288
|
-
'*.log',
|
|
289
|
-
],
|
|
290
|
-
debounceMs: 300,
|
|
291
|
-
defaultBranch: 'main',
|
|
292
|
-
agentId: this.agentId(),
|
|
293
|
-
createdAt: new Date().toISOString(),
|
|
294
|
-
};
|
|
295
|
-
const { writeFileSync } = require('fs');
|
|
296
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Write full ops array
|
|
300
|
-
const { writeFileSync } = require('fs');
|
|
301
|
-
writeFileSync(opsPath, JSON.stringify(this.ops, null, 2));
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
private async hashFileAtCommit(
|
|
305
|
-
commitHash: string,
|
|
306
|
-
filePath: string,
|
|
307
|
-
): Promise<string | undefined> {
|
|
308
|
-
try {
|
|
309
|
-
const reader = new GitReader(this.opts.from);
|
|
310
|
-
const content = reader.readFileContent(commitHash, filePath);
|
|
311
|
-
if (!content) {
|
|
312
|
-
return undefined;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const hashBuffer = await crypto.subtle.digest(
|
|
316
|
-
'SHA-256',
|
|
317
|
-
content as unknown as ArrayBuffer,
|
|
318
|
-
);
|
|
319
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
320
|
-
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
321
|
-
} catch {
|
|
322
|
-
return undefined;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
private agentId(): string {
|
|
327
|
-
return this.opts.agentId ?? `git-import:${this.opts.from}`;
|
|
328
|
-
}
|
|
329
|
-
}
|