agentloom 0.1.2 → 0.1.4
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/README.md +20 -13
- package/dist/cli.js +17 -8
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +14 -0
- package/dist/commands/mcp.js +2 -9
- package/dist/commands/skills.js +6 -27
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.js +82 -11
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +19 -0
- package/dist/core/argv.js +3 -7
- package/dist/core/copy.d.ts +2 -0
- package/dist/core/copy.js +32 -3
- package/dist/core/lockfile.js +34 -2
- package/dist/core/manage-agents-bootstrap.js +1 -0
- package/dist/core/migration.d.ts +28 -0
- package/dist/core/migration.js +809 -0
- package/dist/core/provider-paths.d.ts +16 -0
- package/dist/core/provider-paths.js +154 -0
- package/dist/core/router.d.ts +1 -1
- package/dist/core/router.js +2 -0
- package/dist/core/skills.js +2 -16
- package/dist/core/version-notifier.d.ts +11 -1
- package/dist/core/version-notifier.js +179 -44
- package/dist/sync/index.js +27 -104
- package/dist/types.d.ts +2 -1
- package/dist/types.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# agentloom
|
|
2
2
|
|
|
3
|
-
`agentloom` is a unified CLI for managing agent definitions and MCP configuration across multiple AI coding tools.
|
|
3
|
+
`agentloom` is a unified CLI for managing agent definitions and MCP configuration across multiple AI coding tools — Cursor, Claude, Copilot, Codex, OpenCode, Gemini, and Pi.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
For monorepo-level documentation and architecture context, see the [root README](../../README.md).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
## Getting started
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx agentloom init
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That's all you need. Agentloom picks up your existing provider configs, migrates them into a unified `.agents/` directory, and syncs everything back out to all your tools. From here on, manage your agents, commands, skills, and MCP servers in one place and run `agentloom sync` whenever you make changes.
|
|
13
14
|
|
|
14
15
|
## Install
|
|
15
16
|
|
|
@@ -54,6 +55,7 @@ Global scope uses `~/.agents` with the same file layout.
|
|
|
54
55
|
- `agentloom add <source>`
|
|
55
56
|
- `agentloom find <query>`
|
|
56
57
|
- `agentloom update [source]`
|
|
58
|
+
- `agentloom upgrade`
|
|
57
59
|
- `agentloom sync`
|
|
58
60
|
- `agentloom delete <source|name>`
|
|
59
61
|
|
|
@@ -117,6 +119,7 @@ agentloom --help
|
|
|
117
119
|
agentloom find --help
|
|
118
120
|
agentloom add --help
|
|
119
121
|
agentloom update --help
|
|
122
|
+
agentloom upgrade --help
|
|
120
123
|
agentloom sync --help
|
|
121
124
|
agentloom delete --help
|
|
122
125
|
agentloom agent --help
|
|
@@ -130,12 +133,16 @@ agentloom mcp server --help
|
|
|
130
133
|
|
|
131
134
|
### Version update notice
|
|
132
135
|
|
|
133
|
-
`agentloom`
|
|
136
|
+
`agentloom` performs best-effort npm version checks and upgrades when a newer release is available.
|
|
134
137
|
|
|
135
|
-
-
|
|
136
|
-
- check
|
|
137
|
-
-
|
|
138
|
-
-
|
|
138
|
+
- checks are cached (`~/.agents/.agentloom-version-cache.json`)
|
|
139
|
+
- check/upgrade attempts run at most once every 2 hours per detected version
|
|
140
|
+
- in interactive TTY sessions, agentloom asks before upgrading
|
|
141
|
+
- in non-interactive sessions, upgrades run without prompts
|
|
142
|
+
- after an approved upgrade, the original command is re-run automatically
|
|
143
|
+
- if running from `npx`, auto-upgrade re-runs with `npx agentloom@<latest>`
|
|
144
|
+
- manual upgrade command: `agentloom upgrade`
|
|
145
|
+
- disable auto checks via:
|
|
139
146
|
|
|
140
147
|
```bash
|
|
141
148
|
AGENTLOOM_DISABLE_UPDATE_NOTIFIER=1
|
package/dist/cli.js
CHANGED
|
@@ -5,9 +5,11 @@ import { runAddCommand } from "./commands/add.js";
|
|
|
5
5
|
import { runCommandCommand } from "./commands/command.js";
|
|
6
6
|
import { runDeleteCommand } from "./commands/delete.js";
|
|
7
7
|
import { runFindCommand } from "./commands/find.js";
|
|
8
|
+
import { runInitCommand } from "./commands/init.js";
|
|
8
9
|
import { runMcpCommand } from "./commands/mcp.js";
|
|
9
10
|
import { runSkillCommand } from "./commands/skills.js";
|
|
10
11
|
import { runSyncCommand } from "./commands/sync.js";
|
|
12
|
+
import { runUpgradeCommand } from "./commands/upgrade.js";
|
|
11
13
|
import { runUpdateCommand } from "./commands/update.js";
|
|
12
14
|
import { formatUnknownCommandError, getRootHelpText } from "./core/copy.js";
|
|
13
15
|
import { maybePromptManageAgentsBootstrap } from "./core/manage-agents-bootstrap.js";
|
|
@@ -35,23 +37,24 @@ export async function runCli(argv) {
|
|
|
35
37
|
}
|
|
36
38
|
const parsed = parseArgs(argv);
|
|
37
39
|
const cwd = process.cwd();
|
|
38
|
-
const shouldBootstrapManageAgents = await maybePromptManageAgentsBootstrap({
|
|
39
|
-
command,
|
|
40
|
-
help: Boolean(parsed.help),
|
|
41
|
-
yes: Boolean(parsed.yes),
|
|
42
|
-
cwd,
|
|
43
|
-
});
|
|
44
40
|
const route = parseCommandRoute(argv);
|
|
45
41
|
if (!parsed.help) {
|
|
46
42
|
await maybeNotifyVersionUpdate({
|
|
47
43
|
command,
|
|
44
|
+
argv,
|
|
48
45
|
currentVersion: version,
|
|
49
46
|
});
|
|
50
47
|
}
|
|
51
48
|
if (!route) {
|
|
52
49
|
throw new Error(formatUnknownCommandError(command));
|
|
53
50
|
}
|
|
54
|
-
|
|
51
|
+
const shouldBootstrapManageAgents = await maybePromptManageAgentsBootstrap({
|
|
52
|
+
command,
|
|
53
|
+
help: Boolean(parsed.help),
|
|
54
|
+
yes: Boolean(parsed.yes),
|
|
55
|
+
cwd,
|
|
56
|
+
});
|
|
57
|
+
await runRoutedCommand(route, parsed, cwd, command, version);
|
|
55
58
|
if (shouldBootstrapManageAgents) {
|
|
56
59
|
const bootstrapArgs = buildManageAgentsBootstrapArgs(parsed, cwd);
|
|
57
60
|
await runSkillCommand(parseArgs(bootstrapArgs), cwd);
|
|
@@ -60,7 +63,7 @@ export async function runCli(argv) {
|
|
|
60
63
|
function printHelp() {
|
|
61
64
|
console.log(getRootHelpText());
|
|
62
65
|
}
|
|
63
|
-
async function runRoutedCommand(route, parsed, cwd, command) {
|
|
66
|
+
async function runRoutedCommand(route, parsed, cwd, command, version) {
|
|
64
67
|
if (!route) {
|
|
65
68
|
throw new Error(formatUnknownCommandError(command));
|
|
66
69
|
}
|
|
@@ -75,12 +78,18 @@ async function runRoutedCommand(route, parsed, cwd, command) {
|
|
|
75
78
|
case "update":
|
|
76
79
|
await runUpdateCommand(parsed, cwd);
|
|
77
80
|
return;
|
|
81
|
+
case "upgrade":
|
|
82
|
+
await runUpgradeCommand(parsed, version);
|
|
83
|
+
return;
|
|
78
84
|
case "sync":
|
|
79
85
|
await runSyncCommand(parsed, cwd);
|
|
80
86
|
return;
|
|
81
87
|
case "delete":
|
|
82
88
|
await runDeleteCommand(parsed, cwd);
|
|
83
89
|
return;
|
|
90
|
+
case "init":
|
|
91
|
+
await runInitCommand(parsed, cwd);
|
|
92
|
+
return;
|
|
84
93
|
default:
|
|
85
94
|
throw new Error(formatUnknownCommandError(command));
|
|
86
95
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getInitHelpText } from "../core/copy.js";
|
|
2
|
+
import { runScopedSyncCommand } from "./sync.js";
|
|
3
|
+
export async function runInitCommand(argv, cwd) {
|
|
4
|
+
if (argv.help) {
|
|
5
|
+
console.log(getInitHelpText());
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
await runScopedSyncCommand({
|
|
9
|
+
argv,
|
|
10
|
+
cwd,
|
|
11
|
+
target: "all",
|
|
12
|
+
skipSync: Boolean(argv["no-sync"]),
|
|
13
|
+
});
|
|
14
|
+
}
|
package/dist/commands/mcp.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ALL_PROVIDERS } from "../types.js";
|
|
1
2
|
import { getStringArrayFlag, parseProvidersFlag } from "../core/argv.js";
|
|
2
3
|
import { formatUsageError, getMcpAddHelpText, getMcpDeleteHelpText, getMcpHelpText, getMcpListHelpText, getMcpServerHelpText, } from "../core/copy.js";
|
|
3
4
|
import { readCanonicalMcp, writeCanonicalMcp } from "../core/mcp.js";
|
|
@@ -244,15 +245,7 @@ function runMcpAdd(paths, argv, name) {
|
|
|
244
245
|
}
|
|
245
246
|
else {
|
|
246
247
|
const providerMap = {};
|
|
247
|
-
const
|
|
248
|
-
"cursor",
|
|
249
|
-
"claude",
|
|
250
|
-
"codex",
|
|
251
|
-
"opencode",
|
|
252
|
-
"gemini",
|
|
253
|
-
"copilot",
|
|
254
|
-
];
|
|
255
|
-
for (const provider of allProviders) {
|
|
248
|
+
for (const provider of ALL_PROVIDERS) {
|
|
256
249
|
providerMap[provider] = providers.includes(provider)
|
|
257
250
|
? { ...baseConfig }
|
|
258
251
|
: false;
|
package/dist/commands/skills.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { parseProvidersFlag } from "../core/argv.js";
|
|
3
2
|
import { formatUsageError } from "../core/copy.js";
|
|
4
|
-
import {
|
|
5
|
-
import { formatSyncSummary, resolveProvidersForSync, syncFromCanonical, } from "../sync/index.js";
|
|
3
|
+
import { parseSkillsDir } from "../core/skills.js";
|
|
6
4
|
import { runScopedAddCommand } from "./add.js";
|
|
7
5
|
import { runScopedDeleteCommand } from "./delete.js";
|
|
8
|
-
import {
|
|
6
|
+
import { resolvePathsForCommand } from "./entity-utils.js";
|
|
9
7
|
import { runScopedFindCommand } from "./find.js";
|
|
8
|
+
import { runScopedSyncCommand } from "./sync.js";
|
|
10
9
|
import { runScopedUpdateCommand } from "./update.js";
|
|
11
10
|
export async function runSkillCommand(argv, cwd) {
|
|
12
11
|
const action = argv._[1];
|
|
@@ -79,29 +78,9 @@ export async function runSkillCommand(argv, cwd) {
|
|
|
79
78
|
});
|
|
80
79
|
return;
|
|
81
80
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const providers = await resolveProvidersForSync({
|
|
86
|
-
paths,
|
|
87
|
-
explicitProviders,
|
|
88
|
-
nonInteractive,
|
|
89
|
-
});
|
|
90
|
-
applySkillProviderSideEffects({
|
|
91
|
-
paths,
|
|
92
|
-
providers,
|
|
93
|
-
dryRun: Boolean(argv["dry-run"]),
|
|
94
|
-
warn(message) {
|
|
95
|
-
console.warn(`Warning: ${message}`);
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
const summary = await syncFromCanonical({
|
|
99
|
-
paths,
|
|
100
|
-
providers,
|
|
101
|
-
yes: Boolean(argv.yes),
|
|
102
|
-
nonInteractive,
|
|
103
|
-
dryRun: Boolean(argv["dry-run"]),
|
|
81
|
+
await runScopedSyncCommand({
|
|
82
|
+
argv,
|
|
83
|
+
cwd,
|
|
104
84
|
target: "skill",
|
|
105
85
|
});
|
|
106
|
-
console.log(formatSyncSummary(summary, paths.agentsRoot));
|
|
107
86
|
}
|
package/dist/commands/sync.d.ts
CHANGED
package/dist/commands/sync.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
1
4
|
import { parseProvidersFlag } from "../core/argv.js";
|
|
2
5
|
import { getSyncHelpText } from "../core/copy.js";
|
|
6
|
+
import { formatMigrationSummary, initializeCanonicalLayout, migrateProviderStateToCanonical, MigrationConflictError, } from "../core/migration.js";
|
|
3
7
|
import { getNonInteractiveMode, resolvePathsForCommand, } from "./entity-utils.js";
|
|
4
|
-
import { formatSyncSummary, syncFromCanonical } from "../sync/index.js";
|
|
8
|
+
import { formatSyncSummary, resolveProvidersForSync, syncFromCanonical, } from "../sync/index.js";
|
|
5
9
|
export async function runSyncCommand(argv, cwd) {
|
|
6
10
|
if (argv.help) {
|
|
7
11
|
console.log(getSyncHelpText());
|
|
@@ -14,14 +18,81 @@ export async function runSyncCommand(argv, cwd) {
|
|
|
14
18
|
});
|
|
15
19
|
}
|
|
16
20
|
export async function runScopedSyncCommand(options) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const nonInteractive = getNonInteractiveMode(options.argv);
|
|
22
|
+
let cleanupDryRunPaths;
|
|
23
|
+
try {
|
|
24
|
+
const paths = await resolvePathsForCommand(options.argv, options.cwd);
|
|
25
|
+
const explicitProviders = parseProvidersFlag(options.argv.providers);
|
|
26
|
+
const providers = await resolveProvidersForSync({
|
|
27
|
+
paths,
|
|
28
|
+
explicitProviders,
|
|
29
|
+
nonInteractive,
|
|
30
|
+
});
|
|
31
|
+
const dryRun = Boolean(options.argv["dry-run"]);
|
|
32
|
+
const effectivePaths = dryRun
|
|
33
|
+
? createDryRunCanonicalPaths(paths)
|
|
34
|
+
: { paths, cleanup: undefined };
|
|
35
|
+
cleanupDryRunPaths = effectivePaths.cleanup;
|
|
36
|
+
initializeCanonicalLayout(effectivePaths.paths, providers);
|
|
37
|
+
const migrationSummary = await migrateProviderStateToCanonical({
|
|
38
|
+
paths: effectivePaths.paths,
|
|
39
|
+
providers,
|
|
40
|
+
target: options.target,
|
|
41
|
+
yes: Boolean(options.argv.yes),
|
|
42
|
+
nonInteractive,
|
|
43
|
+
dryRun,
|
|
44
|
+
materializeCanonical: dryRun,
|
|
45
|
+
});
|
|
46
|
+
console.log(formatMigrationSummary(migrationSummary));
|
|
47
|
+
if (options.skipSync) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const syncSummary = await syncFromCanonical({
|
|
51
|
+
paths: effectivePaths.paths,
|
|
52
|
+
providers,
|
|
53
|
+
yes: Boolean(options.argv.yes),
|
|
54
|
+
nonInteractive,
|
|
55
|
+
dryRun,
|
|
56
|
+
target: options.target,
|
|
57
|
+
});
|
|
58
|
+
console.log("");
|
|
59
|
+
console.log(formatSyncSummary(syncSummary, paths.agentsRoot));
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error instanceof MigrationConflictError) {
|
|
63
|
+
console.error(error.message);
|
|
64
|
+
process.exit(2);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
cleanupDryRunPaths?.();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function createDryRunCanonicalPaths(paths) {
|
|
73
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "agentloom-dry-run-"));
|
|
74
|
+
const tempAgentsRoot = path.join(tempRoot, ".agents");
|
|
75
|
+
if (fs.existsSync(paths.agentsRoot) &&
|
|
76
|
+
fs.statSync(paths.agentsRoot).isDirectory()) {
|
|
77
|
+
fs.cpSync(paths.agentsRoot, tempAgentsRoot, {
|
|
78
|
+
recursive: true,
|
|
79
|
+
force: true,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
paths: {
|
|
84
|
+
...paths,
|
|
85
|
+
agentsRoot: tempAgentsRoot,
|
|
86
|
+
agentsDir: path.join(tempAgentsRoot, "agents"),
|
|
87
|
+
commandsDir: path.join(tempAgentsRoot, "commands"),
|
|
88
|
+
skillsDir: path.join(tempAgentsRoot, "skills"),
|
|
89
|
+
mcpPath: path.join(tempAgentsRoot, "mcp.json"),
|
|
90
|
+
lockPath: path.join(tempAgentsRoot, "agents.lock.json"),
|
|
91
|
+
settingsPath: path.join(tempAgentsRoot, "settings.local.json"),
|
|
92
|
+
manifestPath: path.join(tempAgentsRoot, ".sync-manifest.json"),
|
|
93
|
+
},
|
|
94
|
+
cleanup() {
|
|
95
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
96
|
+
},
|
|
97
|
+
};
|
|
27
98
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getUpgradeHelpText } from "../core/copy.js";
|
|
2
|
+
import { upgradeToLatest } from "../core/version-notifier.js";
|
|
3
|
+
export async function runUpgradeCommand(argv, currentVersion) {
|
|
4
|
+
if (argv.help) {
|
|
5
|
+
console.log(getUpgradeHelpText());
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
const result = await upgradeToLatest({ currentVersion });
|
|
9
|
+
if (result === "updated")
|
|
10
|
+
return;
|
|
11
|
+
if (result === "already-latest") {
|
|
12
|
+
console.log(`agentloom ${currentVersion} is already up to date.`);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (result === "unavailable") {
|
|
16
|
+
throw new Error("Unable to reach npm to check the latest agentloom release.");
|
|
17
|
+
}
|
|
18
|
+
throw new Error("Automatic upgrade failed.");
|
|
19
|
+
}
|
package/dist/core/argv.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import minimist from "minimist";
|
|
2
|
+
import { ALL_PROVIDERS } from "../types.js";
|
|
2
3
|
export function parseArgs(argv) {
|
|
3
4
|
const parsed = minimist(argv, {
|
|
4
5
|
boolean: ["global", "local", "yes", "no-sync", "dry-run", "json", "help"],
|
|
@@ -50,16 +51,11 @@ export function parseProvidersFlag(input) {
|
|
|
50
51
|
.map((item) => item.toLowerCase());
|
|
51
52
|
const validProviders = [];
|
|
52
53
|
for (const provider of parsed) {
|
|
53
|
-
if (provider
|
|
54
|
-
provider === "claude" ||
|
|
55
|
-
provider === "codex" ||
|
|
56
|
-
provider === "opencode" ||
|
|
57
|
-
provider === "gemini" ||
|
|
58
|
-
provider === "copilot") {
|
|
54
|
+
if (ALL_PROVIDERS.includes(provider)) {
|
|
59
55
|
validProviders.push(provider);
|
|
60
56
|
continue;
|
|
61
57
|
}
|
|
62
|
-
throw new Error(`Unknown provider: ${provider}. Expected one of:
|
|
58
|
+
throw new Error(`Unknown provider: ${provider}. Expected one of: ${ALL_PROVIDERS.join(", ")}.`);
|
|
63
59
|
}
|
|
64
60
|
return [...new Set(validProviders)];
|
|
65
61
|
}
|
package/dist/core/copy.d.ts
CHANGED
|
@@ -7,7 +7,9 @@ export declare function getRootHelpText(): string;
|
|
|
7
7
|
export declare function getFindHelpText(): string;
|
|
8
8
|
export declare function getAddHelpText(): string;
|
|
9
9
|
export declare function getUpdateHelpText(): string;
|
|
10
|
+
export declare function getUpgradeHelpText(): string;
|
|
10
11
|
export declare function getSyncHelpText(): string;
|
|
12
|
+
export declare function getInitHelpText(): string;
|
|
11
13
|
export declare function getCommandHelpText(): string;
|
|
12
14
|
export declare function getCommandAddHelpText(): string;
|
|
13
15
|
export declare function getCommandListHelpText(): string;
|
package/dist/core/copy.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { ALL_PROVIDERS } from "../types.js";
|
|
2
|
+
const PROVIDERS_CSV = ALL_PROVIDERS.join(",");
|
|
2
3
|
export function getRootHelpText() {
|
|
3
4
|
return `agentloom - unified canonical agent package manager
|
|
4
5
|
|
|
@@ -8,9 +9,11 @@ Usage:
|
|
|
8
9
|
|
|
9
10
|
Aggregate commands:
|
|
10
11
|
add <source> Import agents/commands/mcp/skills from a source
|
|
12
|
+
init Bootstrap canonical files, migrate providers, then sync
|
|
11
13
|
find <query> Search remote + local entities
|
|
12
14
|
update [source] Refresh lockfile-managed imports
|
|
13
|
-
|
|
15
|
+
upgrade Install the latest CLI release
|
|
16
|
+
sync Migrate provider configs then generate provider outputs
|
|
14
17
|
delete <source|name> Delete imported entities by source or name
|
|
15
18
|
|
|
16
19
|
Entity commands:
|
|
@@ -111,8 +114,20 @@ Example:
|
|
|
111
114
|
agentloom update farnoodma/agents --providers codex,cursor
|
|
112
115
|
`;
|
|
113
116
|
}
|
|
117
|
+
export function getUpgradeHelpText() {
|
|
118
|
+
return `Install the latest agentloom release.
|
|
119
|
+
|
|
120
|
+
Usage:
|
|
121
|
+
agentloom upgrade
|
|
122
|
+
|
|
123
|
+
Behavior:
|
|
124
|
+
- Checks npm for the newest published version
|
|
125
|
+
- Upgrades immediately without interactive prompts
|
|
126
|
+
- Reports when the current version is already up to date
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
114
129
|
export function getSyncHelpText() {
|
|
115
|
-
return `
|
|
130
|
+
return `Migrate provider configs into canonical .agents data, then generate provider-specific outputs.
|
|
116
131
|
|
|
117
132
|
Usage:
|
|
118
133
|
agentloom sync [options]
|
|
@@ -125,6 +140,20 @@ Options:
|
|
|
125
140
|
--dry-run Show file changes without writing
|
|
126
141
|
`;
|
|
127
142
|
}
|
|
143
|
+
export function getInitHelpText() {
|
|
144
|
+
return `Bootstrap canonical .agents files, migrate provider configs into canonical state, and sync providers.
|
|
145
|
+
|
|
146
|
+
Usage:
|
|
147
|
+
agentloom init [options]
|
|
148
|
+
|
|
149
|
+
Options:
|
|
150
|
+
--local | --global Choose canonical scope (interactive prompts when omitted)
|
|
151
|
+
--providers <csv> Limit providers (${PROVIDERS_CSV})
|
|
152
|
+
--yes Skip interactive conflict prompts and fail fast in non-interactive flows
|
|
153
|
+
--no-sync Skip sync after canonical bootstrap + migration
|
|
154
|
+
--dry-run Show planned migration/sync changes without writing
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
128
157
|
export function getCommandHelpText() {
|
|
129
158
|
return `Manage canonical command entities.
|
|
130
159
|
|
package/dist/core/lockfile.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { readJsonIfExists, writeJsonAtomic } from "./fs.js";
|
|
2
3
|
const EMPTY_LOCK = {
|
|
3
4
|
version: 1,
|
|
@@ -10,7 +11,7 @@ export function readLockfile(paths) {
|
|
|
10
11
|
}
|
|
11
12
|
return {
|
|
12
13
|
version: 1,
|
|
13
|
-
entries: lock.entries.map((entry) => ({
|
|
14
|
+
entries: lock.entries.map((entry) => normalizeLockEntryForRuntime(paths, {
|
|
14
15
|
...entry,
|
|
15
16
|
importedAgents: Array.isArray(entry.importedAgents)
|
|
16
17
|
? entry.importedAgents
|
|
@@ -66,7 +67,10 @@ function normalizeRenameMap(value) {
|
|
|
66
67
|
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
67
68
|
}
|
|
68
69
|
export function writeLockfile(paths, lockfile) {
|
|
69
|
-
writeJsonAtomic(paths.lockPath,
|
|
70
|
+
writeJsonAtomic(paths.lockPath, {
|
|
71
|
+
version: 1,
|
|
72
|
+
entries: lockfile.entries.map((entry) => normalizeLockEntryForDisk(paths, entry)),
|
|
73
|
+
});
|
|
70
74
|
}
|
|
71
75
|
export function upsertLockEntry(lockfile, entry) {
|
|
72
76
|
const index = lockfile.entries.findIndex((item) => item.source === entry.source &&
|
|
@@ -106,3 +110,31 @@ function normalizeSelectionForKey(value) {
|
|
|
106
110
|
...new Set(value.map((item) => item.trim().toLowerCase()).filter(Boolean)),
|
|
107
111
|
].sort();
|
|
108
112
|
}
|
|
113
|
+
function normalizeLockEntryForRuntime(paths, entry) {
|
|
114
|
+
if (paths.scope !== "local" || entry.sourceType !== "local") {
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
if (path.isAbsolute(entry.source)) {
|
|
118
|
+
return entry;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
...entry,
|
|
122
|
+
source: path.resolve(paths.workspaceRoot, entry.source),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function normalizeLockEntryForDisk(paths, entry) {
|
|
126
|
+
if (paths.scope !== "local" || entry.sourceType !== "local") {
|
|
127
|
+
return entry;
|
|
128
|
+
}
|
|
129
|
+
if (!path.isAbsolute(entry.source)) {
|
|
130
|
+
return {
|
|
131
|
+
...entry,
|
|
132
|
+
source: entry.source || ".",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const relativeSource = path.relative(paths.workspaceRoot, entry.source);
|
|
136
|
+
return {
|
|
137
|
+
...entry,
|
|
138
|
+
source: relativeSource || ".",
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { EntityType, Provider, ScopePaths } from "../types.js";
|
|
2
|
+
export interface MigrationOptions {
|
|
3
|
+
paths: ScopePaths;
|
|
4
|
+
providers: Provider[];
|
|
5
|
+
target: EntityType | "all";
|
|
6
|
+
yes?: boolean;
|
|
7
|
+
nonInteractive?: boolean;
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
materializeCanonical?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface MigrationEntitySummary {
|
|
12
|
+
detected: number;
|
|
13
|
+
imported: number;
|
|
14
|
+
conflicts: number;
|
|
15
|
+
skipped: number;
|
|
16
|
+
}
|
|
17
|
+
export interface MigrationSummary {
|
|
18
|
+
providers: Provider[];
|
|
19
|
+
target: EntityType | "all";
|
|
20
|
+
entities: Record<EntityType, MigrationEntitySummary>;
|
|
21
|
+
}
|
|
22
|
+
export declare class MigrationConflictError extends Error {
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
export declare function createEmptyMigrationSummary(providers: Provider[], target: EntityType | "all"): MigrationSummary;
|
|
26
|
+
export declare function initializeCanonicalLayout(paths: ScopePaths, providers?: Provider[]): void;
|
|
27
|
+
export declare function migrateProviderStateToCanonical(options: MigrationOptions): Promise<MigrationSummary>;
|
|
28
|
+
export declare function formatMigrationSummary(summary: MigrationSummary): string;
|