agentloom 0.1.9 → 0.1.10
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/commands/update.js +31 -2
- package/dist/core/migration.js +33 -10
- package/dist/sync/index.js +63 -24
- package/package.json +1 -1
package/dist/commands/update.js
CHANGED
|
@@ -59,21 +59,28 @@ async function runEntityAwareUpdate(options) {
|
|
|
59
59
|
console.log("No lock entries matched the requested source filter.");
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
|
+
console.log(`Checking ${entries.length} lock entr${entries.length === 1 ? "y" : "ies"} for updates...`);
|
|
62
63
|
let updated = 0;
|
|
63
64
|
let skipped = 0;
|
|
64
|
-
for (const entry of entries) {
|
|
65
|
+
for (const [index, entry] of entries.entries()) {
|
|
66
|
+
const progressPrefix = formatUpdateProgressPrefix(index, entries.length);
|
|
67
|
+
const entryLabel = formatLockEntryLabel(entry);
|
|
65
68
|
if (!entryIncludesTarget(entry, options.target)) {
|
|
69
|
+
console.log(`${progressPrefix} Skipping ${entryLabel} (does not track ${options.target}).`);
|
|
66
70
|
skipped += 1;
|
|
67
71
|
continue;
|
|
68
72
|
}
|
|
73
|
+
console.log(`${progressPrefix} Checking ${entryLabel}...`);
|
|
69
74
|
const probe = prepareSource({
|
|
70
75
|
source: entry.source,
|
|
71
76
|
ref: entry.requestedRef,
|
|
72
77
|
subdir: entry.subdir,
|
|
73
78
|
});
|
|
74
|
-
const
|
|
79
|
+
const latestCommit = probe.resolvedCommit;
|
|
80
|
+
const hasNewCommit = latestCommit !== entry.resolvedCommit;
|
|
75
81
|
probe.cleanup();
|
|
76
82
|
if (!hasNewCommit) {
|
|
83
|
+
console.log(`${progressPrefix} Up to date at ${formatShortCommit(entry.resolvedCommit)}.`);
|
|
77
84
|
skipped += 1;
|
|
78
85
|
continue;
|
|
79
86
|
}
|
|
@@ -83,9 +90,11 @@ async function runEntityAwareUpdate(options) {
|
|
|
83
90
|
!updatePlan.importMcp &&
|
|
84
91
|
!updatePlan.importRules &&
|
|
85
92
|
!updatePlan.importSkills) {
|
|
93
|
+
console.log(`${progressPrefix} Skipping ${entryLabel} (no tracked entities selected for update).`);
|
|
86
94
|
skipped += 1;
|
|
87
95
|
continue;
|
|
88
96
|
}
|
|
97
|
+
console.log(`${progressPrefix} Updating ${entryLabel} (${formatShortCommit(entry.resolvedCommit)} -> ${formatShortCommit(latestCommit)})...`);
|
|
89
98
|
try {
|
|
90
99
|
const importOptions = {
|
|
91
100
|
source: entry.source,
|
|
@@ -142,6 +151,7 @@ async function runEntityAwareUpdate(options) {
|
|
|
142
151
|
rawSource: entry.source,
|
|
143
152
|
summary,
|
|
144
153
|
});
|
|
154
|
+
console.log(`${progressPrefix} Updated ${entryLabel} to ${formatShortCommit(summary.resolvedCommit)}.`);
|
|
145
155
|
updated += 1;
|
|
146
156
|
}
|
|
147
157
|
catch (err) {
|
|
@@ -163,6 +173,25 @@ async function runEntityAwareUpdate(options) {
|
|
|
163
173
|
});
|
|
164
174
|
}
|
|
165
175
|
}
|
|
176
|
+
function formatUpdateProgressPrefix(index, total) {
|
|
177
|
+
return `[${index + 1}/${total}]`;
|
|
178
|
+
}
|
|
179
|
+
function formatLockEntryLabel(entry) {
|
|
180
|
+
const base = entry.source;
|
|
181
|
+
const refSuffix = typeof entry.requestedRef === "string" && entry.requestedRef.length > 0
|
|
182
|
+
? `@${entry.requestedRef}`
|
|
183
|
+
: "";
|
|
184
|
+
const subdirSuffix = typeof entry.subdir === "string" && entry.subdir.length > 0
|
|
185
|
+
? ` (${entry.subdir})`
|
|
186
|
+
: "";
|
|
187
|
+
return `${base}${refSuffix}${subdirSuffix}`;
|
|
188
|
+
}
|
|
189
|
+
function formatShortCommit(commit) {
|
|
190
|
+
const normalized = commit.trim();
|
|
191
|
+
if (normalized.length <= 12)
|
|
192
|
+
return normalized;
|
|
193
|
+
return normalized.slice(0, 12);
|
|
194
|
+
}
|
|
166
195
|
function buildEntryUpdatePlan(entry, target) {
|
|
167
196
|
const includeAgents = shouldUpdateEntity(entry, "agent", target);
|
|
168
197
|
const includeCommands = shouldUpdateEntity(entry, "command", target);
|
package/dist/core/migration.js
CHANGED
|
@@ -914,6 +914,15 @@ async function migrateMcp(options, summary) {
|
|
|
914
914
|
writeCanonicalMcp(options.paths, merged);
|
|
915
915
|
}
|
|
916
916
|
}
|
|
917
|
+
const MANAGED_MCP_CANONICAL_KEYS_BY_PROVIDER = {
|
|
918
|
+
cursor: ["url", "command", "args", "env"],
|
|
919
|
+
claude: ["type", "url", "command", "args", "env"],
|
|
920
|
+
codex: ["url", "command", "args", "env"],
|
|
921
|
+
opencode: ["url", "command", "args", "env"],
|
|
922
|
+
gemini: ["url", "command", "args", "env"],
|
|
923
|
+
copilot: ["type", "url", "command", "args", "env", "tools"],
|
|
924
|
+
pi: ["url", "command", "args", "env"],
|
|
925
|
+
};
|
|
917
926
|
function collectProviderMcpServers(paths, providers) {
|
|
918
927
|
let detected = 0;
|
|
919
928
|
const servers = new Map();
|
|
@@ -930,13 +939,13 @@ function collectProviderMcpServers(paths, providers) {
|
|
|
930
939
|
}
|
|
931
940
|
function readProviderMcp(paths, provider) {
|
|
932
941
|
if (provider === "cursor") {
|
|
933
|
-
return readJsonMcpServers(getCursorMcpPath(paths));
|
|
942
|
+
return readJsonMcpServers(getCursorMcpPath(paths), provider);
|
|
934
943
|
}
|
|
935
944
|
if (provider === "claude") {
|
|
936
|
-
return readJsonMcpServers(getClaudeMcpPath(paths));
|
|
945
|
+
return readJsonMcpServers(getClaudeMcpPath(paths), provider);
|
|
937
946
|
}
|
|
938
947
|
if (provider === "copilot") {
|
|
939
|
-
return readJsonMcpServers(getCopilotMcpPath(paths));
|
|
948
|
+
return readJsonMcpServers(getCopilotMcpPath(paths), provider);
|
|
940
949
|
}
|
|
941
950
|
if (provider === "opencode") {
|
|
942
951
|
return readOpenCodeMcp(getOpenCodeConfigPath(paths));
|
|
@@ -948,16 +957,16 @@ function readProviderMcp(paths, provider) {
|
|
|
948
957
|
return readCodexMcp(getCodexConfigPath(paths));
|
|
949
958
|
}
|
|
950
959
|
if (provider === "pi") {
|
|
951
|
-
return readJsonMcpServers(getPiMcpPath(paths));
|
|
960
|
+
return readJsonMcpServers(getPiMcpPath(paths), provider);
|
|
952
961
|
}
|
|
953
962
|
return {};
|
|
954
963
|
}
|
|
955
|
-
function readJsonMcpServers(filePath) {
|
|
964
|
+
function readJsonMcpServers(filePath, provider) {
|
|
956
965
|
const parsed = readJsonIfExists(filePath);
|
|
957
966
|
if (!parsed || !isObject(parsed.mcpServers)) {
|
|
958
967
|
return {};
|
|
959
968
|
}
|
|
960
|
-
return
|
|
969
|
+
return normalizeManagedMcpServerRecord(parsed.mcpServers, MANAGED_MCP_CANONICAL_KEYS_BY_PROVIDER[provider]);
|
|
961
970
|
}
|
|
962
971
|
function readOpenCodeMcp(filePath) {
|
|
963
972
|
const parsed = readJsonIfExists(filePath);
|
|
@@ -1013,17 +1022,31 @@ function readCodexMcp(filePath) {
|
|
|
1013
1022
|
const parsed = raw.trim() ? TOML.parse(raw) : {};
|
|
1014
1023
|
if (!isObject(parsed.mcp_servers))
|
|
1015
1024
|
return {};
|
|
1016
|
-
return
|
|
1025
|
+
return normalizeManagedMcpServerRecord(parsed.mcp_servers, MANAGED_MCP_CANONICAL_KEYS_BY_PROVIDER.codex);
|
|
1017
1026
|
}
|
|
1018
|
-
function
|
|
1027
|
+
function normalizeManagedMcpServerRecord(raw, allowedKeys) {
|
|
1019
1028
|
const servers = {};
|
|
1020
1029
|
for (const [name, config] of Object.entries(raw)) {
|
|
1021
|
-
|
|
1030
|
+
const normalized = pickManagedMcpFields(config, allowedKeys);
|
|
1031
|
+
if (!normalized)
|
|
1022
1032
|
continue;
|
|
1023
|
-
servers[name] =
|
|
1033
|
+
servers[name] = normalized;
|
|
1024
1034
|
}
|
|
1025
1035
|
return servers;
|
|
1026
1036
|
}
|
|
1037
|
+
function pickManagedMcpFields(config, allowedKeys) {
|
|
1038
|
+
if (!isObject(config)) {
|
|
1039
|
+
return null;
|
|
1040
|
+
}
|
|
1041
|
+
const next = {};
|
|
1042
|
+
for (const key of allowedKeys) {
|
|
1043
|
+
const value = config[key];
|
|
1044
|
+
if (value !== undefined) {
|
|
1045
|
+
next[key] = cloneRecord(value);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
return Object.keys(next).length > 0 ? next : null;
|
|
1049
|
+
}
|
|
1027
1050
|
function normalizeCanonicalServer(server) {
|
|
1028
1051
|
if (!server) {
|
|
1029
1052
|
return {
|
package/dist/sync/index.js
CHANGED
|
@@ -361,8 +361,10 @@ function syncProviderMcp(options) {
|
|
|
361
361
|
const resolved = resolveMcpForProvider(options.mcp, provider);
|
|
362
362
|
if (provider === "cursor") {
|
|
363
363
|
const outputPath = getCursorMcpPath(options.paths);
|
|
364
|
+
const existing = readJsonIfExists(outputPath) ?? {};
|
|
364
365
|
const payload = {
|
|
365
|
-
|
|
366
|
+
...existing,
|
|
367
|
+
mcpServers: mergeManagedMcpServerEntries(existing.mcpServers, mapMcpServers(resolved, ["url", "command", "args", "env"]), ["url", "command", "args", "env"]),
|
|
366
368
|
};
|
|
367
369
|
maybeWriteJson(outputPath, payload, options.dryRun);
|
|
368
370
|
options.generated.add(outputPath);
|
|
@@ -381,19 +383,24 @@ function syncProviderMcp(options) {
|
|
|
381
383
|
continue;
|
|
382
384
|
}
|
|
383
385
|
const mcpPath = getClaudeMcpPath(options.paths);
|
|
384
|
-
const
|
|
386
|
+
const existingMcp = readJsonIfExists(mcpPath) ?? {};
|
|
387
|
+
const managedClaudeServers = mapMcpServers(resolved, [
|
|
385
388
|
"type",
|
|
386
389
|
"url",
|
|
387
390
|
"command",
|
|
388
391
|
"args",
|
|
389
392
|
"env",
|
|
390
393
|
]);
|
|
391
|
-
for (const [serverName, config] of Object.entries(
|
|
394
|
+
for (const [serverName, config] of Object.entries(managedClaudeServers)) {
|
|
392
395
|
if (!("type" in config) && typeof config.url === "string") {
|
|
393
396
|
config.type = "http";
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
|
-
|
|
399
|
+
const claudeServers = mergeManagedMcpServerEntries(existingMcp.mcpServers, managedClaudeServers, ["type", "url", "command", "args", "env"]);
|
|
400
|
+
maybeWriteJson(mcpPath, {
|
|
401
|
+
...existingMcp,
|
|
402
|
+
mcpServers: claudeServers,
|
|
403
|
+
}, options.dryRun);
|
|
397
404
|
options.generated.add(mcpPath);
|
|
398
405
|
settings.enabledMcpjsonServers = Object.keys(claudeServers).sort();
|
|
399
406
|
maybeWriteJson(settingsPath, settings, options.dryRun);
|
|
@@ -403,28 +410,34 @@ function syncProviderMcp(options) {
|
|
|
403
410
|
if (provider === "opencode") {
|
|
404
411
|
const outputPath = getOpenCodeConfigPath(options.paths);
|
|
405
412
|
const existing = readJsonIfExists(outputPath) ?? {};
|
|
406
|
-
const
|
|
413
|
+
const managedMcp = {};
|
|
407
414
|
for (const [serverName, config] of Object.entries(resolved)) {
|
|
408
415
|
if (typeof config.url === "string") {
|
|
409
|
-
|
|
416
|
+
managedMcp[serverName] = {
|
|
410
417
|
type: "remote",
|
|
411
418
|
url: config.url,
|
|
412
419
|
};
|
|
413
420
|
}
|
|
414
421
|
else {
|
|
415
|
-
|
|
422
|
+
managedMcp[serverName] = {
|
|
416
423
|
type: "local",
|
|
417
424
|
command: config.command,
|
|
418
425
|
args: Array.isArray(config.args) ? config.args : undefined,
|
|
419
426
|
};
|
|
420
427
|
}
|
|
421
428
|
if (isObject(config.env)) {
|
|
422
|
-
|
|
429
|
+
managedMcp[serverName].environment = config.env;
|
|
423
430
|
}
|
|
424
431
|
}
|
|
425
432
|
const payload = {
|
|
426
433
|
...existing,
|
|
427
|
-
mcp,
|
|
434
|
+
mcp: mergeManagedMcpServerEntries(existing.mcp, managedMcp, [
|
|
435
|
+
"type",
|
|
436
|
+
"url",
|
|
437
|
+
"command",
|
|
438
|
+
"args",
|
|
439
|
+
"environment",
|
|
440
|
+
]),
|
|
428
441
|
};
|
|
429
442
|
maybeWriteJson(outputPath, payload, options.dryRun);
|
|
430
443
|
options.generated.add(outputPath);
|
|
@@ -453,7 +466,7 @@ function syncProviderMcp(options) {
|
|
|
453
466
|
const payload = {
|
|
454
467
|
...existing,
|
|
455
468
|
experimental,
|
|
456
|
-
mcpServers,
|
|
469
|
+
mcpServers: mergeManagedMcpServerEntries(existing.mcpServers, mcpServers, ["httpUrl", "command", "args", "env"]),
|
|
457
470
|
};
|
|
458
471
|
maybeWriteJson(outputPath, payload, options.dryRun);
|
|
459
472
|
options.generated.add(outputPath);
|
|
@@ -461,6 +474,8 @@ function syncProviderMcp(options) {
|
|
|
461
474
|
}
|
|
462
475
|
if (provider === "copilot") {
|
|
463
476
|
const profileMcpPath = getCopilotMcpPath(options.paths);
|
|
477
|
+
const managedKeys = ["type", "url", "command", "args", "env", "tools"];
|
|
478
|
+
const existingProfileMcp = readJsonIfExists(profileMcpPath) ?? {};
|
|
464
479
|
const copilotServers = mapMcpServers(resolved, [
|
|
465
480
|
"type",
|
|
466
481
|
"url",
|
|
@@ -477,20 +492,25 @@ function syncProviderMcp(options) {
|
|
|
477
492
|
config.type = config.url ? "http" : "local";
|
|
478
493
|
}
|
|
479
494
|
}
|
|
480
|
-
maybeWriteJson(profileMcpPath, {
|
|
495
|
+
maybeWriteJson(profileMcpPath, {
|
|
496
|
+
...existingProfileMcp,
|
|
497
|
+
mcpServers: mergeManagedMcpServerEntries(existingProfileMcp.mcpServers, copilotServers, managedKeys),
|
|
498
|
+
}, options.dryRun);
|
|
481
499
|
options.generated.add(profileMcpPath);
|
|
482
500
|
if (options.paths.scope === "global") {
|
|
483
501
|
const settingsPath = getVsCodeSettingsPath(options.paths.homeDir);
|
|
484
502
|
const settings = readJsonIfExists(settingsPath) ?? {};
|
|
485
|
-
settings["mcp.servers"] = copilotServers;
|
|
503
|
+
settings["mcp.servers"] = mergeManagedMcpServerEntries(settings["mcp.servers"], copilotServers, managedKeys);
|
|
486
504
|
maybeWriteJson(settingsPath, settings, options.dryRun);
|
|
487
505
|
options.generated.add(settingsPath);
|
|
488
506
|
}
|
|
489
507
|
}
|
|
490
508
|
if (provider === "pi") {
|
|
491
509
|
const outputPath = getPiMcpPath(options.paths);
|
|
510
|
+
const existing = readJsonIfExists(outputPath) ?? {};
|
|
492
511
|
const payload = {
|
|
493
|
-
|
|
512
|
+
...existing,
|
|
513
|
+
mcpServers: mergeManagedMcpServerEntries(existing.mcpServers, mapMcpServers(resolved, ["url", "command", "args", "env"]), ["url", "command", "args", "env"]),
|
|
494
514
|
};
|
|
495
515
|
maybeWriteJson(outputPath, payload, options.dryRun);
|
|
496
516
|
options.generated.add(outputPath);
|
|
@@ -813,22 +833,19 @@ function syncCodex(options) {
|
|
|
813
833
|
let nextServers = [...trackedServers];
|
|
814
834
|
if (options.includeMcp) {
|
|
815
835
|
const previousServers = new Set(trackedServers);
|
|
836
|
+
const managedCodexMcpServers = mapMcpServers(options.resolvedMcp, [
|
|
837
|
+
"url",
|
|
838
|
+
"command",
|
|
839
|
+
"args",
|
|
840
|
+
"env",
|
|
841
|
+
]);
|
|
816
842
|
for (const oldServer of previousServers) {
|
|
817
843
|
if (!Object.prototype.hasOwnProperty.call(options.resolvedMcp, oldServer)) {
|
|
818
844
|
delete mcpServers[oldServer];
|
|
819
845
|
}
|
|
820
846
|
}
|
|
821
|
-
for (const [serverName, config] of Object.entries(
|
|
822
|
-
|
|
823
|
-
if (typeof config.url === "string")
|
|
824
|
-
mapped.url = config.url;
|
|
825
|
-
if (typeof config.command === "string")
|
|
826
|
-
mapped.command = config.command;
|
|
827
|
-
if (Array.isArray(config.args))
|
|
828
|
-
mapped.args = config.args;
|
|
829
|
-
if (isObject(config.env))
|
|
830
|
-
mapped.env = config.env;
|
|
831
|
-
mcpServers[serverName] = mapped;
|
|
847
|
+
for (const [serverName, config] of Object.entries(managedCodexMcpServers)) {
|
|
848
|
+
mcpServers[serverName] = mergeManagedMcpServerEntry(mcpServers[serverName], config, ["url", "command", "args", "env"]);
|
|
832
849
|
}
|
|
833
850
|
parsed.mcp_servers = mcpServers;
|
|
834
851
|
nextServers = Object.keys(options.resolvedMcp).sort();
|
|
@@ -895,6 +912,28 @@ function mapMcpServers(servers, allowedKeys) {
|
|
|
895
912
|
}
|
|
896
913
|
return mapped;
|
|
897
914
|
}
|
|
915
|
+
function mergeManagedMcpServerEntries(existingServers, nextServers, managedKeys) {
|
|
916
|
+
const merged = {};
|
|
917
|
+
for (const [serverName, config] of Object.entries(nextServers)) {
|
|
918
|
+
merged[serverName] = mergeManagedMcpServerEntry(isObject(existingServers) ? existingServers[serverName] : undefined, config, managedKeys);
|
|
919
|
+
}
|
|
920
|
+
return merged;
|
|
921
|
+
}
|
|
922
|
+
function mergeManagedMcpServerEntry(existingConfig, nextConfig, managedKeys) {
|
|
923
|
+
const merged = isObject(existingConfig) ? cloneSyncValue(existingConfig) : {};
|
|
924
|
+
for (const key of managedKeys) {
|
|
925
|
+
delete merged[key];
|
|
926
|
+
}
|
|
927
|
+
for (const [key, value] of Object.entries(nextConfig)) {
|
|
928
|
+
if (value !== undefined) {
|
|
929
|
+
merged[key] = cloneSyncValue(value);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return merged;
|
|
933
|
+
}
|
|
934
|
+
function cloneSyncValue(value) {
|
|
935
|
+
return JSON.parse(JSON.stringify(value));
|
|
936
|
+
}
|
|
898
937
|
function maybeWriteJson(filePath, payload, dryRun) {
|
|
899
938
|
if (dryRun)
|
|
900
939
|
return;
|