@hasna/todos 0.11.43 → 0.11.44
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/commands/environment-snapshots.d.ts +3 -0
- package/dist/cli/commands/environment-snapshots.d.ts.map +1 -0
- package/dist/cli/index.js +442 -29
- package/dist/cli-mcp-parity.d.ts +1 -1
- package/dist/cli-mcp-parity.d.ts.map +1 -1
- package/dist/contracts.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +336 -0
- package/dist/json-contracts.d.ts.map +1 -1
- package/dist/lib/environment-snapshots.d.ts +111 -0
- package/dist/lib/environment-snapshots.d.ts.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +335 -7
- package/dist/mcp/token-utils.d.ts.map +1 -1
- package/dist/mcp/tools/environment-snapshots.d.ts +8 -0
- package/dist/mcp/tools/environment-snapshots.d.ts.map +1 -0
- package/dist/mcp.js +2 -0
- package/dist/registry.js +58 -0
- package/dist/release-provenance.json +3 -3
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -10014,6 +10014,295 @@ var init_builtin_templates = __esm(() => {
|
|
|
10014
10014
|
];
|
|
10015
10015
|
});
|
|
10016
10016
|
|
|
10017
|
+
// src/lib/environment-snapshots.ts
|
|
10018
|
+
var exports_environment_snapshots = {};
|
|
10019
|
+
__export(exports_environment_snapshots, {
|
|
10020
|
+
writeEnvironmentSnapshot: () => writeEnvironmentSnapshot,
|
|
10021
|
+
recordEnvironmentSnapshot: () => recordEnvironmentSnapshot,
|
|
10022
|
+
readEnvironmentSnapshot: () => readEnvironmentSnapshot,
|
|
10023
|
+
compareEnvironmentSnapshots: () => compareEnvironmentSnapshots,
|
|
10024
|
+
compareEnvironmentSnapshotFiles: () => compareEnvironmentSnapshotFiles,
|
|
10025
|
+
captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
|
|
10026
|
+
});
|
|
10027
|
+
import { createHash as createHash5 } from "crypto";
|
|
10028
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync6 } from "fs";
|
|
10029
|
+
import { hostname, platform, arch } from "os";
|
|
10030
|
+
import { dirname as dirname7, join as join7, resolve as resolve9 } from "path";
|
|
10031
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
10032
|
+
function sha2563(value) {
|
|
10033
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
10034
|
+
}
|
|
10035
|
+
function fileRecord(root, relativePath) {
|
|
10036
|
+
const path = join7(root, relativePath);
|
|
10037
|
+
if (!existsSync8(path))
|
|
10038
|
+
return null;
|
|
10039
|
+
const stat = statSync6(path);
|
|
10040
|
+
if (!stat.isFile())
|
|
10041
|
+
return null;
|
|
10042
|
+
const content = readFileSync6(path);
|
|
10043
|
+
return { path: relativePath, sha256: sha2563(content), size_bytes: content.length };
|
|
10044
|
+
}
|
|
10045
|
+
function manifestRecord(root, relativePath) {
|
|
10046
|
+
const base = fileRecord(root, relativePath);
|
|
10047
|
+
if (!base)
|
|
10048
|
+
return null;
|
|
10049
|
+
const parsed = readJsonFile(join7(root, relativePath));
|
|
10050
|
+
if (!parsed)
|
|
10051
|
+
return { ...base, redacted: {} };
|
|
10052
|
+
const redacted = redactValue({
|
|
10053
|
+
name: parsed["name"] ?? null,
|
|
10054
|
+
version: parsed["version"] ?? null,
|
|
10055
|
+
packageManager: parsed["packageManager"] ?? null,
|
|
10056
|
+
scripts: parsed["scripts"] ?? {},
|
|
10057
|
+
dependencies: parsed["dependencies"] ?? {},
|
|
10058
|
+
devDependencies: parsed["devDependencies"] ?? {},
|
|
10059
|
+
peerDependencies: parsed["peerDependencies"] ?? {},
|
|
10060
|
+
optionalDependencies: parsed["optionalDependencies"] ?? {}
|
|
10061
|
+
});
|
|
10062
|
+
return { ...base, redacted };
|
|
10063
|
+
}
|
|
10064
|
+
function runLocalCommand(root, args) {
|
|
10065
|
+
const result = Bun.spawnSync({
|
|
10066
|
+
cmd: args,
|
|
10067
|
+
cwd: root,
|
|
10068
|
+
stdout: "pipe",
|
|
10069
|
+
stderr: "pipe",
|
|
10070
|
+
env: { PATH: process.env["PATH"] || "" }
|
|
10071
|
+
});
|
|
10072
|
+
return {
|
|
10073
|
+
exitCode: result.exitCode,
|
|
10074
|
+
stdout: redactEvidenceText(result.stdout.toString("utf8").trim()),
|
|
10075
|
+
stderr: redactEvidenceText(result.stderr.toString("utf8").trim())
|
|
10076
|
+
};
|
|
10077
|
+
}
|
|
10078
|
+
function summarizeGitStatus(lines) {
|
|
10079
|
+
const summary = { added: 0, modified: 0, deleted: 0, renamed: 0, untracked: 0 };
|
|
10080
|
+
for (const line of lines) {
|
|
10081
|
+
if (line.startsWith("??"))
|
|
10082
|
+
summary.untracked += 1;
|
|
10083
|
+
else if (line.includes("R"))
|
|
10084
|
+
summary.renamed += 1;
|
|
10085
|
+
else if (line.includes("D"))
|
|
10086
|
+
summary.deleted += 1;
|
|
10087
|
+
else if (line.includes("A"))
|
|
10088
|
+
summary.added += 1;
|
|
10089
|
+
else if (line.includes("M"))
|
|
10090
|
+
summary.modified += 1;
|
|
10091
|
+
}
|
|
10092
|
+
return summary;
|
|
10093
|
+
}
|
|
10094
|
+
function captureGit(root, warnings) {
|
|
10095
|
+
const inside = runLocalCommand(root, ["git", "rev-parse", "--is-inside-work-tree"]);
|
|
10096
|
+
if (inside.exitCode !== 0 || inside.stdout !== "true") {
|
|
10097
|
+
return { present: false, branch: null, commit: null, is_dirty: false, status_porcelain: [], status_summary: summarizeGitStatus([]) };
|
|
10098
|
+
}
|
|
10099
|
+
const branch = runLocalCommand(root, ["git", "branch", "--show-current"]);
|
|
10100
|
+
const commit = runLocalCommand(root, ["git", "rev-parse", "HEAD"]);
|
|
10101
|
+
const status = runLocalCommand(root, ["git", "status", "--porcelain=v1"]);
|
|
10102
|
+
if (commit.exitCode !== 0)
|
|
10103
|
+
warnings.push(`git commit unavailable: ${commit.stderr || commit.stdout || "unknown error"}`);
|
|
10104
|
+
if (status.exitCode !== 0)
|
|
10105
|
+
warnings.push(`git status unavailable: ${status.stderr || status.stdout || "unknown error"}`);
|
|
10106
|
+
const lines = status.stdout ? status.stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
10107
|
+
return {
|
|
10108
|
+
present: true,
|
|
10109
|
+
branch: branch.stdout || null,
|
|
10110
|
+
commit: commit.stdout || null,
|
|
10111
|
+
is_dirty: lines.length > 0,
|
|
10112
|
+
status_porcelain: lines,
|
|
10113
|
+
status_summary: summarizeGitStatus(lines)
|
|
10114
|
+
};
|
|
10115
|
+
}
|
|
10116
|
+
function packageManager(env, lockfiles) {
|
|
10117
|
+
const userAgent = (env["npm_config_user_agent"] || "").toLowerCase();
|
|
10118
|
+
if (userAgent.includes("bun"))
|
|
10119
|
+
return "bun";
|
|
10120
|
+
if (lockfiles.some((file) => file.path.startsWith("bun.lock")))
|
|
10121
|
+
return "bun";
|
|
10122
|
+
if (userAgent.includes("npm") || lockfiles.some((file) => file.path.includes("package-lock")))
|
|
10123
|
+
return "npm";
|
|
10124
|
+
return "unknown";
|
|
10125
|
+
}
|
|
10126
|
+
function isSecretEnvKey(key) {
|
|
10127
|
+
return /api[_-]?key|token|secret|password|credential|private|session|cookie/i.test(key);
|
|
10128
|
+
}
|
|
10129
|
+
function commandEnv(env, includeValues) {
|
|
10130
|
+
const keys = Object.keys(env).sort();
|
|
10131
|
+
const interesting = keys.filter((key) => isSecretEnvKey(key) || ["CI", "NODE_ENV", "BUN_ENV", "SHELL", "TERM", "PATH", "PWD", "USER", "npm_config_user_agent"].includes(key) || key.startsWith("TODOS_") || key.startsWith("BUN_"));
|
|
10132
|
+
const redactedKeys = interesting.filter(isSecretEnvKey);
|
|
10133
|
+
const values = includeValues ? Object.fromEntries(interesting.map((key) => [key, isSecretEnvKey(key) ? "[REDACTED]" : redactEvidenceText(String(env[key] ?? ""))])) : null;
|
|
10134
|
+
return {
|
|
10135
|
+
command: null,
|
|
10136
|
+
env_keys: interesting,
|
|
10137
|
+
env: values,
|
|
10138
|
+
redacted_keys: redactedKeys
|
|
10139
|
+
};
|
|
10140
|
+
}
|
|
10141
|
+
function defaultSnapshotDir() {
|
|
10142
|
+
const dbPath = getDatabasePath();
|
|
10143
|
+
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
10144
|
+
return join7(tmpdir2(), "hasna-todos", "environment-snapshots");
|
|
10145
|
+
return join7(dirname7(resolve9(dbPath)), "environment-snapshots");
|
|
10146
|
+
}
|
|
10147
|
+
function snapshotWithId(snapshot) {
|
|
10148
|
+
const digest = sha2563(JSON.stringify(snapshot)).slice(0, 24);
|
|
10149
|
+
return { id: `env_${digest}`, ...snapshot };
|
|
10150
|
+
}
|
|
10151
|
+
function captureEnvironmentSnapshot(input = {}) {
|
|
10152
|
+
const root = resolve9(input.root || process.cwd());
|
|
10153
|
+
const env = input.env || process.env;
|
|
10154
|
+
const warnings = [];
|
|
10155
|
+
const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
|
|
10156
|
+
const lockfiles = LOCKFILES.map((file) => fileRecord(root, file)).filter((file) => Boolean(file));
|
|
10157
|
+
const configHashes = CONFIG_FILES.map((file) => fileRecord(root, file)).filter((file) => Boolean(file));
|
|
10158
|
+
const commandMetadata = commandEnv(env, Boolean(input.include_env_values));
|
|
10159
|
+
commandMetadata.command = input.command ? redactEvidenceText(input.command) : null;
|
|
10160
|
+
if (manifests.length === 0)
|
|
10161
|
+
warnings.push("no package manifest found");
|
|
10162
|
+
if (lockfiles.length === 0)
|
|
10163
|
+
warnings.push("no package lockfile found");
|
|
10164
|
+
return snapshotWithId({
|
|
10165
|
+
schema_version: 1,
|
|
10166
|
+
captured_at: input.now ? new Date(input.now).toISOString() : new Date().toISOString(),
|
|
10167
|
+
root,
|
|
10168
|
+
machine: { hostname: hostname(), platform: platform(), arch: arch() },
|
|
10169
|
+
target: {
|
|
10170
|
+
task_id: input.task_id ?? null,
|
|
10171
|
+
run_id: input.run_id ?? null,
|
|
10172
|
+
agent_id: input.agent_id ?? null
|
|
10173
|
+
},
|
|
10174
|
+
runtime: {
|
|
10175
|
+
bun: Bun.version || null,
|
|
10176
|
+
node: process.version,
|
|
10177
|
+
executable: process.execPath
|
|
10178
|
+
},
|
|
10179
|
+
package_manager: {
|
|
10180
|
+
manager: packageManager(env, lockfiles),
|
|
10181
|
+
user_agent: env["npm_config_user_agent"] ? redactEvidenceText(env["npm_config_user_agent"]) : null,
|
|
10182
|
+
manifests,
|
|
10183
|
+
lockfiles
|
|
10184
|
+
},
|
|
10185
|
+
git: captureGit(root, warnings),
|
|
10186
|
+
config_hashes: configHashes,
|
|
10187
|
+
command_env: commandMetadata,
|
|
10188
|
+
warnings
|
|
10189
|
+
});
|
|
10190
|
+
}
|
|
10191
|
+
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
10192
|
+
const path = outputPath ? resolve9(outputPath) : join7(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
10193
|
+
ensureDir2(dirname7(path));
|
|
10194
|
+
writeJsonFile(path, snapshot);
|
|
10195
|
+
return path;
|
|
10196
|
+
}
|
|
10197
|
+
function readEnvironmentSnapshot(path) {
|
|
10198
|
+
const snapshot = readJsonFile(resolve9(path));
|
|
10199
|
+
if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
|
|
10200
|
+
throw new Error(`Invalid environment snapshot: ${path}`);
|
|
10201
|
+
}
|
|
10202
|
+
return snapshot;
|
|
10203
|
+
}
|
|
10204
|
+
function recordEnvironmentSnapshot(input = {}, db) {
|
|
10205
|
+
let taskId = input.task_id;
|
|
10206
|
+
let runId = input.run_id;
|
|
10207
|
+
const needsDatabase = Boolean(taskId || runId);
|
|
10208
|
+
const d = needsDatabase ? db || getDatabase() : null;
|
|
10209
|
+
if (runId) {
|
|
10210
|
+
runId = resolveTaskRunId(runId, d);
|
|
10211
|
+
const run = getTaskRun(runId, d);
|
|
10212
|
+
if (!run)
|
|
10213
|
+
throw new Error(`Run not found: ${input.run_id}`);
|
|
10214
|
+
taskId = taskId || run.task_id;
|
|
10215
|
+
}
|
|
10216
|
+
if (taskId && d) {
|
|
10217
|
+
taskId = resolvePartialId(d, "tasks", taskId) || taskId;
|
|
10218
|
+
if (!getTask(taskId, d))
|
|
10219
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
10220
|
+
}
|
|
10221
|
+
const snapshot = captureEnvironmentSnapshot({ ...input, task_id: taskId, run_id: runId });
|
|
10222
|
+
const outputPath = writeEnvironmentSnapshot(snapshot, input.output_path);
|
|
10223
|
+
let taskVerificationId = null;
|
|
10224
|
+
let runArtifactId = null;
|
|
10225
|
+
if (runId) {
|
|
10226
|
+
const artifact = addTaskRunArtifact({
|
|
10227
|
+
run_id: runId,
|
|
10228
|
+
path: outputPath,
|
|
10229
|
+
artifact_type: "environment_snapshot",
|
|
10230
|
+
description: "Reproducible local environment snapshot",
|
|
10231
|
+
metadata: { environment_snapshot_id: snapshot.id, schema_version: snapshot.schema_version },
|
|
10232
|
+
store_content: input.store_content ?? true,
|
|
10233
|
+
agent_id: input.agent_id
|
|
10234
|
+
}, d);
|
|
10235
|
+
runArtifactId = artifact.id;
|
|
10236
|
+
} else if (taskId) {
|
|
10237
|
+
const verification = addTaskVerification({
|
|
10238
|
+
task_id: taskId,
|
|
10239
|
+
command: input.command || "capture environment snapshot",
|
|
10240
|
+
status: "unknown",
|
|
10241
|
+
output_summary: `environment snapshot ${snapshot.id}`,
|
|
10242
|
+
artifact_path: outputPath,
|
|
10243
|
+
agent_id: input.agent_id,
|
|
10244
|
+
run_at: snapshot.captured_at
|
|
10245
|
+
}, d);
|
|
10246
|
+
taskVerificationId = verification.id;
|
|
10247
|
+
}
|
|
10248
|
+
return { snapshot, output_path: outputPath, task_verification_id: taskVerificationId, run_artifact_id: runArtifactId };
|
|
10249
|
+
}
|
|
10250
|
+
function keyed(files) {
|
|
10251
|
+
return new Map(files.map((file) => [file.path, file]));
|
|
10252
|
+
}
|
|
10253
|
+
function diffFiles(left, right) {
|
|
10254
|
+
const leftMap = keyed(left);
|
|
10255
|
+
const rightMap = keyed(right);
|
|
10256
|
+
const paths = [...new Set([...leftMap.keys(), ...rightMap.keys()])].sort((a, b) => a.localeCompare(b));
|
|
10257
|
+
return paths.map((path) => ({ path, left_sha256: leftMap.get(path)?.sha256 ?? null, right_sha256: rightMap.get(path)?.sha256 ?? null })).filter((entry) => entry.left_sha256 !== entry.right_sha256);
|
|
10258
|
+
}
|
|
10259
|
+
function compareEnvironmentSnapshots(left, right) {
|
|
10260
|
+
const warnings = [];
|
|
10261
|
+
if (left.schema_version !== right.schema_version)
|
|
10262
|
+
warnings.push("snapshot schema versions differ");
|
|
10263
|
+
return {
|
|
10264
|
+
schema_version: 1,
|
|
10265
|
+
left_id: left.id,
|
|
10266
|
+
right_id: right.id,
|
|
10267
|
+
same_root: left.root === right.root,
|
|
10268
|
+
same_machine: left.machine.hostname === right.machine.hostname && left.machine.platform === right.machine.platform && left.machine.arch === right.machine.arch,
|
|
10269
|
+
same_runtime: left.runtime.bun === right.runtime.bun && left.runtime.node === right.runtime.node,
|
|
10270
|
+
same_git_commit: left.git.commit === right.git.commit,
|
|
10271
|
+
dirty_state_changed: left.git.is_dirty !== right.git.is_dirty,
|
|
10272
|
+
changed_config_hashes: diffFiles(left.config_hashes, right.config_hashes),
|
|
10273
|
+
changed_lockfiles: diffFiles(left.package_manager.lockfiles, right.package_manager.lockfiles),
|
|
10274
|
+
changed_manifests: diffFiles(left.package_manager.manifests, right.package_manager.manifests),
|
|
10275
|
+
warnings
|
|
10276
|
+
};
|
|
10277
|
+
}
|
|
10278
|
+
function compareEnvironmentSnapshotFiles(leftPath, rightPath) {
|
|
10279
|
+
return compareEnvironmentSnapshots(readEnvironmentSnapshot(leftPath), readEnvironmentSnapshot(rightPath));
|
|
10280
|
+
}
|
|
10281
|
+
var MANIFEST_FILES, LOCKFILES, CONFIG_FILES;
|
|
10282
|
+
var init_environment_snapshots = __esm(() => {
|
|
10283
|
+
init_task_runs();
|
|
10284
|
+
init_task_commits();
|
|
10285
|
+
init_database();
|
|
10286
|
+
init_tasks();
|
|
10287
|
+
init_sync_utils();
|
|
10288
|
+
MANIFEST_FILES = ["package.json", "dashboard/package.json", "sdk/package.json"];
|
|
10289
|
+
LOCKFILES = ["bun.lock", "bun.lockb", "package-lock.json", "npm-shrinkwrap.json"];
|
|
10290
|
+
CONFIG_FILES = [
|
|
10291
|
+
"AGENTS.md",
|
|
10292
|
+
"CLAUDE.md",
|
|
10293
|
+
"README.md",
|
|
10294
|
+
"SECURITY.md",
|
|
10295
|
+
"bunfig.toml",
|
|
10296
|
+
"tsconfig.json",
|
|
10297
|
+
"components.json",
|
|
10298
|
+
"next.config.js",
|
|
10299
|
+
"next.config.mjs",
|
|
10300
|
+
"next.config.ts",
|
|
10301
|
+
"vite.config.ts",
|
|
10302
|
+
"dashboard/vite.config.ts"
|
|
10303
|
+
];
|
|
10304
|
+
});
|
|
10305
|
+
|
|
10017
10306
|
// src/mcp/index.ts
|
|
10018
10307
|
init_agents();
|
|
10019
10308
|
init_database();
|
|
@@ -14385,6 +14674,8 @@ var MCP_TOOL_GROUPS = {
|
|
|
14385
14674
|
"check_file_lock",
|
|
14386
14675
|
"create_comment",
|
|
14387
14676
|
"create_handoff",
|
|
14677
|
+
"capture_environment_snapshot",
|
|
14678
|
+
"compare_environment_snapshots",
|
|
14388
14679
|
"create_inbox_item",
|
|
14389
14680
|
"delete_comment",
|
|
14390
14681
|
"detect_file_relationships",
|
|
@@ -21853,19 +22144,55 @@ ${lines.join(`
|
|
|
21853
22144
|
}
|
|
21854
22145
|
}
|
|
21855
22146
|
|
|
22147
|
+
// src/mcp/tools/environment-snapshots.ts
|
|
22148
|
+
function registerEnvironmentSnapshotTools(server, { shouldRegisterTool, formatError }) {
|
|
22149
|
+
if (shouldRegisterTool("capture_environment_snapshot")) {
|
|
22150
|
+
server.tool("capture_environment_snapshot", "Capture a local reproducible environment snapshot with Bun/node versions, package-manager state, git status, config hashes, command metadata, and redacted manifests. Optionally attach it to a local task or run.", {
|
|
22151
|
+
root: exports_external.string().optional(),
|
|
22152
|
+
task_id: exports_external.string().optional(),
|
|
22153
|
+
run_id: exports_external.string().optional(),
|
|
22154
|
+
agent_id: exports_external.string().optional(),
|
|
22155
|
+
command: exports_external.string().optional(),
|
|
22156
|
+
output_path: exports_external.string().optional(),
|
|
22157
|
+
include_env_values: exports_external.boolean().optional()
|
|
22158
|
+
}, async (params) => {
|
|
22159
|
+
try {
|
|
22160
|
+
const { recordEnvironmentSnapshot: recordEnvironmentSnapshot2 } = await Promise.resolve().then(() => (init_environment_snapshots(), exports_environment_snapshots));
|
|
22161
|
+
const result = recordEnvironmentSnapshot2(params);
|
|
22162
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
22163
|
+
} catch (e) {
|
|
22164
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22165
|
+
}
|
|
22166
|
+
});
|
|
22167
|
+
}
|
|
22168
|
+
if (shouldRegisterTool("compare_environment_snapshots")) {
|
|
22169
|
+
server.tool("compare_environment_snapshots", "Compare two local environment snapshot JSON files and report runtime, git, manifest, lockfile, and config hash drift.", {
|
|
22170
|
+
left_path: exports_external.string(),
|
|
22171
|
+
right_path: exports_external.string()
|
|
22172
|
+
}, async ({ left_path, right_path }) => {
|
|
22173
|
+
try {
|
|
22174
|
+
const { compareEnvironmentSnapshotFiles: compareEnvironmentSnapshotFiles2 } = await Promise.resolve().then(() => (init_environment_snapshots(), exports_environment_snapshots));
|
|
22175
|
+
return { content: [{ type: "text", text: JSON.stringify(compareEnvironmentSnapshotFiles2(left_path, right_path), null, 2) }] };
|
|
22176
|
+
} catch (e) {
|
|
22177
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
22178
|
+
}
|
|
22179
|
+
});
|
|
22180
|
+
}
|
|
22181
|
+
}
|
|
22182
|
+
|
|
21856
22183
|
// src/lib/package-version.ts
|
|
21857
|
-
import { existsSync as
|
|
21858
|
-
import { dirname as
|
|
22184
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
22185
|
+
import { dirname as dirname8, join as join8 } from "path";
|
|
21859
22186
|
import { fileURLToPath } from "url";
|
|
21860
22187
|
function getPackageVersion(fromUrl = import.meta.url) {
|
|
21861
22188
|
try {
|
|
21862
|
-
let dir =
|
|
22189
|
+
let dir = dirname8(fileURLToPath(fromUrl));
|
|
21863
22190
|
for (let i = 0;i < 5; i++) {
|
|
21864
|
-
const pkgPath =
|
|
21865
|
-
if (
|
|
21866
|
-
return JSON.parse(
|
|
22191
|
+
const pkgPath = join8(dir, "package.json");
|
|
22192
|
+
if (existsSync9(pkgPath)) {
|
|
22193
|
+
return JSON.parse(readFileSync7(pkgPath, "utf-8")).version || "0.0.0";
|
|
21867
22194
|
}
|
|
21868
|
-
const parent =
|
|
22195
|
+
const parent = dirname8(dir);
|
|
21869
22196
|
if (parent === dir)
|
|
21870
22197
|
break;
|
|
21871
22198
|
dir = parent;
|
|
@@ -22046,6 +22373,7 @@ registerTaskRelTools(server, toolContext);
|
|
|
22046
22373
|
registerCodeTools(server, toolContext);
|
|
22047
22374
|
registerAgentTools(server, { ...toolContext, agentFocusMap });
|
|
22048
22375
|
registerTemplateTools(server, toolContext);
|
|
22376
|
+
registerEnvironmentSnapshotTools(server, toolContext);
|
|
22049
22377
|
registerMachineTools(server, { shouldRegisterTool, formatError });
|
|
22050
22378
|
registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError });
|
|
22051
22379
|
async function main() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../src/mcp/token-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE3C,eAAO,MAAM,cAAc,aAoBzB,CAAC;AAEH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../src/mcp/token-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;AAE3C,eAAO,MAAM,cAAc,aAoBzB,CAAC;AAEH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAuP7D,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAMhE,CAAC;AAgBF,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,EACZ,YAAY,qBAA+B,EAC3C,UAAU,qBAAmC,GAC5C,OAAO,CAqCT;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,SAAM,GAAG,MAAM,GAAG,IAAI,CAK5F;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,SAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgB1F;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqCtF;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAexD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAMD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,UAA4B,GAAG,OAAO,CA0BvH;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAY5D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
type Helpers = {
|
|
3
|
+
shouldRegisterTool: (name: string) => boolean;
|
|
4
|
+
formatError: (e: unknown) => string;
|
|
5
|
+
};
|
|
6
|
+
export declare function registerEnvironmentSnapshotTools(server: McpServer, { shouldRegisterTool, formatError }: Helpers): void;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=environment-snapshots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-snapshots.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/environment-snapshots.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,KAAK,OAAO,GAAG;IACb,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,WAAW,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,CAAC;CACrC,CAAC;AAEF,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,kBAAkB,EAAE,WAAW,EAAE,EAAE,OAAO,GAAG,IAAI,CA4CtH"}
|
package/dist/mcp.js
CHANGED
package/dist/registry.js
CHANGED
|
@@ -2433,6 +2433,50 @@ var TODOS_JSON_CONTRACTS = [
|
|
|
2433
2433
|
},
|
|
2434
2434
|
optional: {}
|
|
2435
2435
|
}),
|
|
2436
|
+
contract({
|
|
2437
|
+
id: "environment_snapshot",
|
|
2438
|
+
name: "Environment Snapshot",
|
|
2439
|
+
description: "Local reproducibility snapshot for task and run verification context.",
|
|
2440
|
+
surfaces: ["cli", "mcp", "sdk"],
|
|
2441
|
+
stability: "stable",
|
|
2442
|
+
required: {
|
|
2443
|
+
schema_version: field("integer", "Snapshot schema version."),
|
|
2444
|
+
id: field("string", "Content-derived snapshot identifier."),
|
|
2445
|
+
captured_at: isoDateField,
|
|
2446
|
+
root: field("string", "Canonical local project root inspected."),
|
|
2447
|
+
machine: field("object", "Local hostname, platform, and architecture metadata."),
|
|
2448
|
+
target: field("object", "Optional task, run, and agent attachment IDs."),
|
|
2449
|
+
runtime: field("object", "Bun, Node, and executable metadata."),
|
|
2450
|
+
package_manager: field("object", "Detected package manager, lockfile hashes, and redacted manifests."),
|
|
2451
|
+
git: field("object", "Local git branch, commit, dirty state, and porcelain status."),
|
|
2452
|
+
config_hashes: field("array", "SHA-256 hashes of relevant local config files."),
|
|
2453
|
+
command_env: field("object", "Redacted command and environment metadata."),
|
|
2454
|
+
warnings: field("array", "Warnings about missing or unavailable local data.")
|
|
2455
|
+
},
|
|
2456
|
+
optional: {}
|
|
2457
|
+
}),
|
|
2458
|
+
contract({
|
|
2459
|
+
id: "environment_snapshot_comparison",
|
|
2460
|
+
name: "Environment Snapshot Comparison",
|
|
2461
|
+
description: "Drift summary returned when comparing two local environment snapshots.",
|
|
2462
|
+
surfaces: ["cli", "mcp", "sdk"],
|
|
2463
|
+
stability: "stable",
|
|
2464
|
+
required: {
|
|
2465
|
+
schema_version: field("integer", "Comparison schema version."),
|
|
2466
|
+
left_id: field("string", "Left snapshot ID."),
|
|
2467
|
+
right_id: field("string", "Right snapshot ID."),
|
|
2468
|
+
same_root: field("boolean", "Whether both snapshots were captured for the same root."),
|
|
2469
|
+
same_machine: field("boolean", "Whether hostname, platform, and architecture match."),
|
|
2470
|
+
same_runtime: field("boolean", "Whether Bun and Node versions match."),
|
|
2471
|
+
same_git_commit: field("boolean", "Whether git commit IDs match."),
|
|
2472
|
+
dirty_state_changed: field("boolean", "Whether the git dirty flag changed."),
|
|
2473
|
+
changed_config_hashes: field("array", "Changed config hash records."),
|
|
2474
|
+
changed_lockfiles: field("array", "Changed lockfile hash records."),
|
|
2475
|
+
changed_manifests: field("array", "Changed manifest hash records."),
|
|
2476
|
+
warnings: field("array", "Comparison warnings.")
|
|
2477
|
+
},
|
|
2478
|
+
optional: {}
|
|
2479
|
+
}),
|
|
2436
2480
|
contract({
|
|
2437
2481
|
id: "local_event_hook",
|
|
2438
2482
|
name: "Local Event Hook",
|
|
@@ -7288,6 +7332,8 @@ var MCP_TOOL_GROUPS = {
|
|
|
7288
7332
|
"check_file_lock",
|
|
7289
7333
|
"create_comment",
|
|
7290
7334
|
"create_handoff",
|
|
7335
|
+
"capture_environment_snapshot",
|
|
7336
|
+
"compare_environment_snapshots",
|
|
7291
7337
|
"create_inbox_item",
|
|
7292
7338
|
"delete_comment",
|
|
7293
7339
|
"detect_file_relationships",
|
|
@@ -8262,6 +8308,18 @@ var TODOS_CLI_MCP_PARITY = [
|
|
|
8262
8308
|
mcpTool: "build_agent_context_pack"
|
|
8263
8309
|
}
|
|
8264
8310
|
},
|
|
8311
|
+
{
|
|
8312
|
+
domain: "environment-snapshots",
|
|
8313
|
+
cliCommands: ["todos env-snapshot"],
|
|
8314
|
+
mcpTools: ["capture_environment_snapshot", "compare_environment_snapshots"],
|
|
8315
|
+
jsonContracts: ["environment_snapshot", "environment_snapshot_comparison", "structured_error", "api_error"],
|
|
8316
|
+
errorContracts: ["structured_error", "api_error"],
|
|
8317
|
+
status: "matched",
|
|
8318
|
+
example: {
|
|
8319
|
+
cli: "todos env-snapshot capture --task 1234abcd --json",
|
|
8320
|
+
mcpTool: "capture_environment_snapshot"
|
|
8321
|
+
}
|
|
8322
|
+
},
|
|
8265
8323
|
{
|
|
8266
8324
|
domain: "imports",
|
|
8267
8325
|
cliCommands: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packageName": "@hasna/todos",
|
|
3
|
-
"packageVersion": "0.11.
|
|
3
|
+
"packageVersion": "0.11.44",
|
|
4
4
|
"repository": "https://github.com/hasna/todos.git",
|
|
5
|
-
"gitCommit": "
|
|
6
|
-
"generatedAt": "2026-05-21T17:
|
|
5
|
+
"gitCommit": "f6bc0dc0b71478ba56d4d044eb632f261ec623b4",
|
|
6
|
+
"generatedAt": "2026-05-21T17:49:00.069Z"
|
|
7
7
|
}
|