me2lon 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +62 -2
- package/dist/api.d.ts +35 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +286 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +261 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +23 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +272 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +173 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +31 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/presentation.d.ts +12 -0
- package/dist/presentation.d.ts.map +1 -0
- package/dist/presentation.js +50 -0
- package/dist/presentation.js.map +1 -0
- package/dist/safe-output.d.ts +20 -0
- package/dist/safe-output.d.ts.map +1 -0
- package/dist/safe-output.js +770 -0
- package/dist/safe-output.js.map +1 -0
- package/dist/setup.d.ts +14 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +92 -0
- package/dist/setup.js.map +1 -0
- package/dist/skill.d.ts +2 -0
- package/dist/skill.d.ts.map +1 -0
- package/dist/skill.js +30 -0
- package/dist/skill.js.map +1 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/workspace-source.d.ts +11 -0
- package/dist/workspace-source.d.ts.map +1 -0
- package/dist/workspace-source.js +387 -0
- package/dist/workspace-source.js.map +1 -0
- package/package.json +43 -4
- package/skill/LICENSE +21 -0
- package/skill/SKILL.md +18 -0
- package/src/api.ts +426 -0
- package/src/cli.ts +305 -0
- package/src/client.ts +413 -0
- package/src/config.ts +252 -0
- package/src/errors.ts +40 -0
- package/src/index.ts +37 -0
- package/src/presentation.ts +39 -0
- package/src/safe-output.ts +826 -0
- package/src/setup.ts +95 -0
- package/src/skill.ts +22 -0
- package/src/types.ts +85 -0
- package/src/workspace-source.ts +526 -0
- package/tsconfig.build.json +18 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstat, open, readFile, readlink, realpath } from "node:fs/promises";
|
|
3
|
+
import { constants } from "node:fs";
|
|
4
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { Me2lonClientError } from "./errors.js";
|
|
7
|
+
import { LOCAL_SOURCE_LIMITS, SOURCE_SCHEMA_VERSION, SNAPSHOT_SCHEMA_VERSION, } from "./types.js";
|
|
8
|
+
const MAX_GIT_OUTPUT_BYTES = 64 * 1024 * 1024;
|
|
9
|
+
const exactCommitPattern = /^[0-9a-f]{40}$/u;
|
|
10
|
+
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
11
|
+
async function runGit(root, args) {
|
|
12
|
+
return await new Promise((resolvePromise, reject) => {
|
|
13
|
+
const child = spawn("git", ["-C", root, ...args], {
|
|
14
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
15
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0", LC_ALL: "C" },
|
|
16
|
+
windowsHide: true,
|
|
17
|
+
});
|
|
18
|
+
const stdout = [];
|
|
19
|
+
let stdoutBytes = 0;
|
|
20
|
+
let stderrBytes = 0;
|
|
21
|
+
child.stdout.on("data", (chunk) => {
|
|
22
|
+
stdoutBytes += chunk.length;
|
|
23
|
+
if (stdoutBytes > MAX_GIT_OUTPUT_BYTES) {
|
|
24
|
+
child.kill();
|
|
25
|
+
reject(new Me2lonClientError("ME2LON_GIT_OUTPUT_LIMIT", "Git produced more repository metadata than the Me2lon client accepts."));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
stdout.push(chunk);
|
|
29
|
+
});
|
|
30
|
+
child.stderr.on("data", (chunk) => {
|
|
31
|
+
stderrBytes += chunk.length;
|
|
32
|
+
if (stderrBytes > 64 * 1024) {
|
|
33
|
+
child.kill();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
child.once("error", (error) => {
|
|
37
|
+
reject(new Me2lonClientError("ME2LON_GIT_UNAVAILABLE", "The Me2lon client could not run Git.", { cause: error }));
|
|
38
|
+
});
|
|
39
|
+
child.once("close", (code) => {
|
|
40
|
+
if (code !== 0) {
|
|
41
|
+
reject(new Me2lonClientError("ME2LON_GIT_FAILURE", "Git could not inspect the bound workspace and base commit."));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
resolvePromise(Buffer.concat(stdout));
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export async function discoverWorkspace(directory) {
|
|
49
|
+
const root = await realpath(decodeUtf8(await runGit(resolve(directory), ["rev-parse", "--show-toplevel"]), "Git root").trim());
|
|
50
|
+
const head = decodeUtf8(await runGit(root, ["rev-parse", "--verify", "HEAD^{commit}"]), "Git HEAD").trim();
|
|
51
|
+
if (!exactCommitPattern.test(head))
|
|
52
|
+
throw new Me2lonClientError("ME2LON_BASE_COMMIT_INVALID", "A local Git repository with an initial commit is required.");
|
|
53
|
+
const branch = decodeUtf8(await runGit(root, ["rev-parse", "--abbrev-ref", "HEAD"]), "Git branch").trim();
|
|
54
|
+
return { root, head, branch: branch === "HEAD" ? null : branch };
|
|
55
|
+
}
|
|
56
|
+
async function snapshotFile(root, path, maximumBytes) {
|
|
57
|
+
const absolute = pathInsideRoot(root, path);
|
|
58
|
+
let parent = root;
|
|
59
|
+
for (const part of path.split("/")) {
|
|
60
|
+
parent = resolve(parent, part);
|
|
61
|
+
if ((await lstat(parent)).isSymbolicLink()) {
|
|
62
|
+
throw new Me2lonClientError("ME2LON_SOURCE_SYMLINK_UNSUPPORTED", `Symbolic link ${path} cannot be uploaded in a full snapshot.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!(await lstat(absolute)).isFile())
|
|
66
|
+
throw new Me2lonClientError("ME2LON_SOURCE_KIND_UNSUPPORTED", `Path ${path} is not a regular file; submodules and special files are unsupported.`);
|
|
67
|
+
const handle = await open(absolute, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
68
|
+
try {
|
|
69
|
+
const before = await handle.stat();
|
|
70
|
+
if (!before.isFile() || before.size > maximumBytes)
|
|
71
|
+
throw new Me2lonClientError("ME2LON_SOURCE_FILE_LIMIT", `File ${path} is unsupported or exceeds the upload limit.`);
|
|
72
|
+
const buffer = Buffer.alloc(maximumBytes + 1);
|
|
73
|
+
let length = 0;
|
|
74
|
+
while (length < buffer.length) {
|
|
75
|
+
const { bytesRead } = await handle.read(buffer, length, buffer.length - length, null);
|
|
76
|
+
if (bytesRead === 0)
|
|
77
|
+
break;
|
|
78
|
+
length += bytesRead;
|
|
79
|
+
}
|
|
80
|
+
if (length > maximumBytes)
|
|
81
|
+
throw new Me2lonClientError("ME2LON_SOURCE_FILE_LIMIT", `File ${path} exceeds the upload limit.`);
|
|
82
|
+
const bytes = buffer.subarray(0, length);
|
|
83
|
+
const after = await handle.stat();
|
|
84
|
+
if (before.size !== after.size || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs ||
|
|
85
|
+
await realpath(absolute) !== absolute) {
|
|
86
|
+
throw new Me2lonClientError("ME2LON_WORKSPACE_CHANGED_DURING_READ", "The workspace changed during capture; retry the check.");
|
|
87
|
+
}
|
|
88
|
+
return bytes;
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await handle.close();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Capture every supported Git-visible file, and verify the complete capture twice. */
|
|
95
|
+
export async function buildWorkspaceSnapshot(workspace, limits = LOCAL_SOURCE_LIMITS) {
|
|
96
|
+
const before = await discoverWorkspace(workspace.root);
|
|
97
|
+
if (before.root !== workspace.root)
|
|
98
|
+
throw new Me2lonClientError("ME2LON_WORKSPACE_NOT_ROOT", "The bound Git root changed.");
|
|
99
|
+
const visible = await gitVisiblePaths(before.root);
|
|
100
|
+
const flags = await trackedPathFlags(before.root);
|
|
101
|
+
assertCompleteWorktree(flags);
|
|
102
|
+
const paths = [...await currentExistingPaths(before.root, visible)].sort(comparePaths);
|
|
103
|
+
checkLimits(paths.length, 0, 0, limits);
|
|
104
|
+
const files = [];
|
|
105
|
+
const captured = new Map();
|
|
106
|
+
let totalReplacementBytes = 0;
|
|
107
|
+
for (const path of paths) {
|
|
108
|
+
const bytes = await snapshotFile(before.root, path, limits.maxFileBytes);
|
|
109
|
+
totalReplacementBytes += bytes.length;
|
|
110
|
+
checkLimits(files.length + 1, bytes.length, totalReplacementBytes, limits);
|
|
111
|
+
const content = decodeUtf8(bytes, `File ${path}`);
|
|
112
|
+
if (content.includes("\0"))
|
|
113
|
+
throw new Me2lonClientError("ME2LON_SOURCE_NOT_UTF8", `File ${path} contains NUL bytes and cannot be uploaded.`);
|
|
114
|
+
files.push({ path, content });
|
|
115
|
+
captured.set(path, bytes);
|
|
116
|
+
}
|
|
117
|
+
for (const [path, bytes] of captured) {
|
|
118
|
+
if (!bytes.equals(await snapshotFile(before.root, path, limits.maxFileBytes))) {
|
|
119
|
+
throw new Me2lonClientError("ME2LON_WORKSPACE_CHANGED_DURING_READ", "The workspace changed during capture; retry the check.");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const after = await discoverWorkspace(before.root);
|
|
123
|
+
if (before.head !== after.head || before.branch !== after.branch ||
|
|
124
|
+
!visible.equals(await gitVisiblePaths(before.root)) || !flags.equals(await trackedPathFlags(before.root))) {
|
|
125
|
+
throw new Me2lonClientError("ME2LON_WORKSPACE_CHANGED_DURING_READ", "Git changed during capture; retry the check.");
|
|
126
|
+
}
|
|
127
|
+
if (files.length === 0)
|
|
128
|
+
throw new Me2lonClientError("ME2LON_SOURCE_EMPTY", "The project has no files to upload.");
|
|
129
|
+
return {
|
|
130
|
+
schemaVersion: SNAPSHOT_SCHEMA_VERSION, baseCommitSha: before.head, branchLabel: before.branch, files,
|
|
131
|
+
totalReplacementBytes,
|
|
132
|
+
digest: `sha256:${createHash("sha256").update(JSON.stringify({ head: before.head, files })).digest("hex")}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function decodeUtf8(bytes, context) {
|
|
136
|
+
try {
|
|
137
|
+
return utf8Decoder.decode(bytes);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
throw new Me2lonClientError("ME2LON_SOURCE_NOT_UTF8", `${context} is not valid UTF-8 and cannot be represented by ${SOURCE_SCHEMA_VERSION}.`, { cause: error });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function parseNullDelimited(bytes, context) {
|
|
144
|
+
const decoded = decodeUtf8(bytes, context);
|
|
145
|
+
const values = decoded.split("\0");
|
|
146
|
+
if (values.at(-1) === "") {
|
|
147
|
+
values.pop();
|
|
148
|
+
}
|
|
149
|
+
return values;
|
|
150
|
+
}
|
|
151
|
+
function validateGitPath(path) {
|
|
152
|
+
if (path === "" ||
|
|
153
|
+
path.includes("\0") ||
|
|
154
|
+
isAbsolute(path) ||
|
|
155
|
+
path.split("/").some((part) => part === "..") ||
|
|
156
|
+
path === ".git" ||
|
|
157
|
+
path.startsWith(".git/")) {
|
|
158
|
+
throw new Me2lonClientError("ME2LON_SOURCE_PATH_INVALID", "Git reported a path that cannot be uploaded safely.");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function pathInsideRoot(root, gitPath) {
|
|
162
|
+
validateGitPath(gitPath);
|
|
163
|
+
const absolutePath = resolve(root, ...gitPath.split("/"));
|
|
164
|
+
const fromRoot = relative(root, absolutePath);
|
|
165
|
+
if (fromRoot.startsWith(`..${sep}`) || fromRoot === ".." || isAbsolute(fromRoot)) {
|
|
166
|
+
throw new Me2lonClientError("ME2LON_SOURCE_PATH_INVALID", "Git reported a path outside the bound workspace.");
|
|
167
|
+
}
|
|
168
|
+
return absolutePath;
|
|
169
|
+
}
|
|
170
|
+
function comparePaths(left, right) {
|
|
171
|
+
return Buffer.compare(Buffer.from(left, "utf8"), Buffer.from(right, "utf8"));
|
|
172
|
+
}
|
|
173
|
+
async function baseTree(workspace) {
|
|
174
|
+
const raw = await runGit(workspace.root, [
|
|
175
|
+
"ls-tree",
|
|
176
|
+
"-r",
|
|
177
|
+
"-z",
|
|
178
|
+
"--full-tree",
|
|
179
|
+
workspace.baseCommitSha,
|
|
180
|
+
]);
|
|
181
|
+
const entries = new Map();
|
|
182
|
+
for (const record of parseNullDelimited(raw, "A Git tree path")) {
|
|
183
|
+
const tab = record.indexOf("\t");
|
|
184
|
+
if (tab === -1) {
|
|
185
|
+
throw new Me2lonClientError("ME2LON_GIT_METADATA_INVALID", "Git returned an invalid base-tree record.");
|
|
186
|
+
}
|
|
187
|
+
const metadata = record.slice(0, tab).split(" ");
|
|
188
|
+
const path = record.slice(tab + 1);
|
|
189
|
+
validateGitPath(path);
|
|
190
|
+
const [mode, type, oid] = metadata;
|
|
191
|
+
if (!mode || !type || !oid) {
|
|
192
|
+
throw new Me2lonClientError("ME2LON_GIT_METADATA_INVALID", "Git returned incomplete base-tree metadata.");
|
|
193
|
+
}
|
|
194
|
+
entries.set(path, { mode, type, oid });
|
|
195
|
+
}
|
|
196
|
+
return entries;
|
|
197
|
+
}
|
|
198
|
+
async function gitVisiblePaths(root) {
|
|
199
|
+
return await runGit(root, [
|
|
200
|
+
"ls-files",
|
|
201
|
+
"-z",
|
|
202
|
+
"--cached",
|
|
203
|
+
"--others",
|
|
204
|
+
"--exclude-standard",
|
|
205
|
+
]);
|
|
206
|
+
}
|
|
207
|
+
async function trackedPathFlags(root) {
|
|
208
|
+
return await runGit(root, ["ls-files", "-v", "-z"]);
|
|
209
|
+
}
|
|
210
|
+
function assertCompleteWorktree(raw) {
|
|
211
|
+
for (const record of parseNullDelimited(raw, "A Git tracked-path flag")) {
|
|
212
|
+
const tag = record.at(0);
|
|
213
|
+
if (tag === "S" || (tag !== undefined && /^[a-z]$/u.test(tag))) {
|
|
214
|
+
throw new Me2lonClientError("ME2LON_INCOMPLETE_WORKTREE", "The bound repository uses skip-worktree or assume-unchanged entries, so Me2lon cannot construct a complete source revision.");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function changedPaths(workspace) {
|
|
219
|
+
return await runGit(workspace.root, [
|
|
220
|
+
"diff",
|
|
221
|
+
"--name-only",
|
|
222
|
+
"-z",
|
|
223
|
+
"--no-renames",
|
|
224
|
+
"--no-ext-diff",
|
|
225
|
+
"--no-textconv",
|
|
226
|
+
workspace.baseCommitSha,
|
|
227
|
+
"--",
|
|
228
|
+
]);
|
|
229
|
+
}
|
|
230
|
+
async function currentExistingPaths(root, raw) {
|
|
231
|
+
const result = new Set();
|
|
232
|
+
for (const path of parseNullDelimited(raw, "A Git worktree path")) {
|
|
233
|
+
validateGitPath(path);
|
|
234
|
+
const absolutePath = pathInsideRoot(root, path);
|
|
235
|
+
try {
|
|
236
|
+
await lstat(absolutePath);
|
|
237
|
+
result.add(path);
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
if (error.code !== "ENOENT") {
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
async function readBaseBlob(root, oid) {
|
|
248
|
+
return await runGit(root, ["cat-file", "blob", oid]);
|
|
249
|
+
}
|
|
250
|
+
function checkLimits(changes, fileBytes, totalBytes, limits) {
|
|
251
|
+
if (changes > limits.maxChanges) {
|
|
252
|
+
throw new Me2lonClientError("ME2LON_SOURCE_CHANGE_LIMIT", `The workspace revision exceeds the ${limits.maxChanges}-change client limit.`);
|
|
253
|
+
}
|
|
254
|
+
if (fileBytes > limits.maxFileBytes) {
|
|
255
|
+
throw new Me2lonClientError("ME2LON_SOURCE_FILE_LIMIT", `A replacement exceeds the ${limits.maxFileBytes}-byte client limit.`);
|
|
256
|
+
}
|
|
257
|
+
if (totalBytes > limits.maxTotalBytes) {
|
|
258
|
+
throw new Me2lonClientError("ME2LON_SOURCE_TOTAL_LIMIT", `The workspace revision exceeds the ${limits.maxTotalBytes}-byte aggregate client limit.`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
export async function bindWorkspace(requestedRoot, repositoryId, requestedBaseCommit) {
|
|
262
|
+
if (!repositoryId.trim()) {
|
|
263
|
+
throw new Me2lonClientError("ME2LON_REPOSITORY_REQUIRED", "A repository id is required.");
|
|
264
|
+
}
|
|
265
|
+
if (!exactCommitPattern.test(requestedBaseCommit)) {
|
|
266
|
+
throw new Me2lonClientError("ME2LON_BASE_COMMIT_NOT_EXACT", "The base commit must be a full 40-character hexadecimal Git commit id, not a branch or abbreviated revision.");
|
|
267
|
+
}
|
|
268
|
+
const root = await realpath(resolve(requestedRoot));
|
|
269
|
+
const reportedRoot = decodeUtf8(await runGit(root, ["rev-parse", "--show-toplevel"]), "The Git workspace root").trim();
|
|
270
|
+
const canonicalRoot = await realpath(reportedRoot);
|
|
271
|
+
if (canonicalRoot !== root) {
|
|
272
|
+
throw new Me2lonClientError("ME2LON_WORKSPACE_NOT_ROOT", "The MCP workspace must be bound to the Git repository root, not a parent or subdirectory.");
|
|
273
|
+
}
|
|
274
|
+
const canonicalCommit = decodeUtf8(await runGit(root, [
|
|
275
|
+
"rev-parse",
|
|
276
|
+
"--verify",
|
|
277
|
+
`${requestedBaseCommit}^{commit}`,
|
|
278
|
+
]), "The Git base commit").trim();
|
|
279
|
+
if (!exactCommitPattern.test(canonicalCommit)) {
|
|
280
|
+
throw new Me2lonClientError("ME2LON_BASE_COMMIT_INVALID", "Git did not resolve the requested object to an exact commit id.");
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
root,
|
|
284
|
+
repositoryId: repositoryId.trim(),
|
|
285
|
+
baseCommitSha: canonicalCommit,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
export async function buildWorkspaceSource(workspace, limits = LOCAL_SOURCE_LIMITS) {
|
|
289
|
+
const [tree, visibleBeforeRaw, changedBeforeRaw, pathFlagsRaw] = await Promise.all([
|
|
290
|
+
baseTree(workspace),
|
|
291
|
+
gitVisiblePaths(workspace.root),
|
|
292
|
+
changedPaths(workspace),
|
|
293
|
+
trackedPathFlags(workspace.root),
|
|
294
|
+
]);
|
|
295
|
+
assertCompleteWorktree(pathFlagsRaw);
|
|
296
|
+
const visible = await currentExistingPaths(workspace.root, visibleBeforeRaw);
|
|
297
|
+
const candidatePaths = new Set(parseNullDelimited(changedBeforeRaw, "A changed Git path"));
|
|
298
|
+
for (const path of visible) {
|
|
299
|
+
if (!tree.has(path)) {
|
|
300
|
+
candidatePaths.add(path);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const path of tree.keys()) {
|
|
304
|
+
if (!visible.has(path)) {
|
|
305
|
+
candidatePaths.add(path);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const changes = [];
|
|
309
|
+
let replacementCount = 0;
|
|
310
|
+
let deletionCount = 0;
|
|
311
|
+
let totalReplacementBytes = 0;
|
|
312
|
+
for (const path of [...candidatePaths].sort(comparePaths)) {
|
|
313
|
+
validateGitPath(path);
|
|
314
|
+
const base = tree.get(path);
|
|
315
|
+
if (!visible.has(path)) {
|
|
316
|
+
if (base) {
|
|
317
|
+
changes.push({ operation: "delete", path });
|
|
318
|
+
deletionCount += 1;
|
|
319
|
+
checkLimits(changes.length, 0, totalReplacementBytes, limits);
|
|
320
|
+
}
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
const absolutePath = pathInsideRoot(workspace.root, path);
|
|
324
|
+
const before = await lstat(absolutePath);
|
|
325
|
+
if (before.isSymbolicLink()) {
|
|
326
|
+
if (!base || base.mode !== "120000" || base.type !== "blob") {
|
|
327
|
+
throw new Me2lonClientError("ME2LON_SOURCE_SYMLINK_UNSUPPORTED", `Changed symbolic link ${path} cannot be represented by ${SOURCE_SCHEMA_VERSION}.`);
|
|
328
|
+
}
|
|
329
|
+
const [target, original] = await Promise.all([
|
|
330
|
+
readlink(absolutePath, "utf8"),
|
|
331
|
+
readBaseBlob(workspace.root, base.oid),
|
|
332
|
+
]);
|
|
333
|
+
if (!Buffer.from(target, "utf8").equals(original)) {
|
|
334
|
+
throw new Me2lonClientError("ME2LON_SOURCE_SYMLINK_UNSUPPORTED", `Changed symbolic link ${path} cannot be represented by ${SOURCE_SCHEMA_VERSION}.`);
|
|
335
|
+
}
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (base?.type === "commit") {
|
|
339
|
+
throw new Me2lonClientError("ME2LON_SOURCE_SUBMODULE_UNSUPPORTED", `Changed submodule ${path} cannot be represented by ${SOURCE_SCHEMA_VERSION}.`);
|
|
340
|
+
}
|
|
341
|
+
if (!before.isFile()) {
|
|
342
|
+
throw new Me2lonClientError("ME2LON_SOURCE_KIND_UNSUPPORTED", `Changed path ${path} is not a regular UTF-8 file.`);
|
|
343
|
+
}
|
|
344
|
+
checkLimits(changes.length, before.size, totalReplacementBytes, limits);
|
|
345
|
+
const bytes = await readFile(absolutePath);
|
|
346
|
+
const after = await lstat(absolutePath);
|
|
347
|
+
if (before.size !== after.size ||
|
|
348
|
+
before.mtimeMs !== after.mtimeMs ||
|
|
349
|
+
before.ctimeMs !== after.ctimeMs) {
|
|
350
|
+
throw new Me2lonClientError("ME2LON_WORKSPACE_CHANGED_DURING_READ", "The workspace changed while Me2lon was preparing its revision; retry the check.");
|
|
351
|
+
}
|
|
352
|
+
checkLimits(changes.length + 1, bytes.length, totalReplacementBytes + bytes.length, limits);
|
|
353
|
+
if (base?.type === "blob") {
|
|
354
|
+
const original = await readBaseBlob(workspace.root, base.oid);
|
|
355
|
+
if (bytes.equals(original)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const content = decodeUtf8(bytes, `Replacement ${path}`);
|
|
360
|
+
changes.push({ operation: "replace", path, content });
|
|
361
|
+
replacementCount += 1;
|
|
362
|
+
totalReplacementBytes += bytes.length;
|
|
363
|
+
}
|
|
364
|
+
const [visibleAfterRaw, changedAfterRaw] = await Promise.all([
|
|
365
|
+
gitVisiblePaths(workspace.root),
|
|
366
|
+
changedPaths(workspace),
|
|
367
|
+
]);
|
|
368
|
+
if (!visibleBeforeRaw.equals(visibleAfterRaw) ||
|
|
369
|
+
!changedBeforeRaw.equals(changedAfterRaw)) {
|
|
370
|
+
throw new Me2lonClientError("ME2LON_WORKSPACE_CHANGED_DURING_READ", "The workspace changed while Me2lon was preparing its revision; retry the check.");
|
|
371
|
+
}
|
|
372
|
+
const digestPayload = JSON.stringify({
|
|
373
|
+
schemaVersion: SOURCE_SCHEMA_VERSION,
|
|
374
|
+
baseCommitSha: workspace.baseCommitSha,
|
|
375
|
+
changes,
|
|
376
|
+
});
|
|
377
|
+
return {
|
|
378
|
+
schemaVersion: SOURCE_SCHEMA_VERSION,
|
|
379
|
+
baseCommitSha: workspace.baseCommitSha,
|
|
380
|
+
changes,
|
|
381
|
+
digest: `sha256:${createHash("sha256").update(digestPayload).digest("hex")}`,
|
|
382
|
+
replacementCount,
|
|
383
|
+
deletionCount,
|
|
384
|
+
totalReplacementBytes,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
//# sourceMappingURL=workspace-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-source.js","sourceRoot":"","sources":["../src/workspace-source.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "me2lon",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI and portable agent skill for the Me2lon API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=22.14.0"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"me2lon": "dist/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src",
|
|
22
|
+
"!src/mcp.ts",
|
|
23
|
+
"skill",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"tsconfig.json",
|
|
27
|
+
"tsconfig.build.json"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "node --input-type=module -e \"import {rm} from 'node:fs/promises'; await rm('dist', {recursive:true, force:true})\" && tsc -p tsconfig.build.json",
|
|
31
|
+
"prepack": "npm run build",
|
|
32
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
33
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
34
|
+
"verify": "npm run typecheck && npm test && npm run build"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^24.0.0",
|
|
38
|
+
"tsx": "^4.20.0",
|
|
39
|
+
"typescript": "^5.9.0",
|
|
40
|
+
"@modelcontextprotocol/sdk": "1.32.2",
|
|
41
|
+
"zod": "4.6.7"
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"homepage": "https://me2lon.clarus.me/cli"
|
|
45
|
+
}
|
package/skill/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Guillaume Claret
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: me2lon
|
|
3
|
+
description: Analyze a local JavaScript or TypeScript backend through the Me2lon CLI, retrieve findings, and explain their evidence and limitations. Use when the user requests Me2lon analysis or results.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Me2lon
|
|
7
|
+
|
|
8
|
+
Use the installed `me2lon` CLI. Install with `npm install -g me2lon` when requested.
|
|
9
|
+
The default service is https://me2lon.clarus.me and currently requires an invited GitHub identity.
|
|
10
|
+
|
|
11
|
+
1. Work from the intended Git root, with an initial commit. Snapshots include tracked and nonignored untracked files and uncommitted changes. Limits: 2,000 files, 1 MiB per file, 8 MiB total. Binary files, symlinks, submodules and sparse/assume-unchanged entries are rejected. Do not change ignore rules or omit source just to make analysis pass. `--project-root` selects analysis scope; it does not filter the upload.
|
|
12
|
+
2. If authorization is needed, run `me2lon setup --json`. Give the browser link and code from stderr to the human and keep setup running while they approve. Never approve on their behalf or ask them to paste tokens. Renewal preserves project history; a revoked credential needs `setup --new-credential`.
|
|
13
|
+
3. Run `me2lon analyze --wait --json`, optionally with `--project-root PATH`. Progress goes to stderr; the final result is JSON on stdout. Preserve job and workspace revision IDs. For ambiguous selection, use `analyze --revision-id ID --project-root PATH` on the existing snapshot.
|
|
14
|
+
4. Resume with `me2lon get-job --job-id ID --wait --json`. The default waiting timeout is two hours; use `--timeout-seconds` if needed. Interrupting the client does not cancel the job. Cancellation requires `me2lon cancel-job --job-id ID` and the user's intent to cancel.
|
|
15
|
+
5. Explain findings, checked scope, limitations, and unsupported or pending work. `jobStatus` is execution state; `resultStatus` is the analysis assessment. A successful job does not mean every property is proved. Distinguish formal evidence from AI assessments and unsuccessful attempts; general analysis does not establish policy compliance.
|
|
16
|
+
6. After requested source changes, analyze again to capture the new revision. Do not edit the source or repeatedly resubmit failed jobs without a concrete reason within the user's task.
|
|
17
|
+
|
|
18
|
+
Credentials live outside the source tree. Never print their contents. Use `me2lon --help` for additional commands; policy approval remains a human action.
|