loadout-ai 0.6.0 → 0.7.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/README.md +122 -19
- package/catalog/discovered.json +25557 -24308
- package/dist/src/cli.js +2 -2
- package/dist/src/commands/agents.js +6 -23
- package/dist/src/commands/catalog.js +130 -33
- package/dist/src/commands/health.js +6 -6
- package/dist/src/commands/inventory.js +19 -120
- package/dist/src/commands/lifecycle.js +13 -51
- package/dist/src/commands/mcp.js +10 -73
- package/dist/src/commands/setup.js +5 -5
- package/dist/src/commands/sharing.js +11 -108
- package/dist/src/commands/support.js +20 -9
- package/dist/src/core/{adapters.js → agents/adapters.js} +2 -2
- package/dist/src/core/{agent-inspection.js → agents/agent-inspection.js} +26 -6
- package/dist/src/core/{codex-mcp.js → agents/codex-mcp.js} +1 -1
- package/dist/src/core/{health-score-evidence.js → agents/health-score-evidence.js} +8 -8
- package/dist/src/core/{runtime-tools.js → agents/runtime-tools.js} +2 -2
- package/dist/src/core/{audit.js → catalog/audit.js} +4 -4
- package/dist/src/core/{catalog.js → catalog/catalog.js} +5 -5
- package/dist/src/core/{components.js → catalog/components.js} +2 -2
- package/dist/src/core/{conformance.js → catalog/conformance.js} +2 -2
- package/dist/src/core/{profiles.js → catalog/profiles.js} +2 -2
- package/dist/src/core/{provenance.js → catalog/provenance.js} +3 -3
- package/dist/src/core/{registry.js → catalog/registry.js} +1 -1
- package/dist/src/core/{skill-compare.js → catalog/skill-compare.js} +1 -1
- package/dist/src/core/{skill-inventory.js → catalog/skill-inventory.js} +3 -3
- package/dist/src/core/{skills.js → catalog/skills.js} +1 -1
- package/dist/src/core/{candidate-intelligence.js → discovery/candidate-intelligence.js} +6 -6
- package/dist/src/core/{discovery-connector.js → discovery/discovery-connector.js} +2 -2
- package/dist/src/core/{evaluate.js → discovery/evaluate.js} +2 -2
- package/dist/src/core/{mcp-registry-discovery.js → discovery/mcp-registry-discovery.js} +1 -1
- package/dist/src/core/{observations.js → discovery/observations.js} +3 -3
- package/dist/src/core/{review-queue.js → discovery/review-queue.js} +2 -2
- package/dist/src/core/{skills-sh-discovery.js → discovery/skills-sh-discovery.js} +1 -1
- package/dist/src/core/{adopt.js → install/adopt.js} +3 -3
- package/dist/src/core/{catalog-install.js → install/catalog-install.js} +11 -11
- package/dist/src/core/{file-lock.js → install/file-lock.js} +1 -1
- package/dist/src/core/{install.js → install/install.js} +5 -5
- package/dist/src/core/{package.js → install/package.js} +3 -3
- package/dist/src/core/{reconcile.js → install/reconcile.js} +5 -5
- package/dist/src/core/{remove.js → install/remove.js} +4 -4
- package/dist/src/core/{snapshot.js → install/snapshot.js} +1 -1
- package/dist/src/core/{source.js → install/source.js} +1 -1
- package/dist/src/core/{sync.js → install/sync.js} +9 -9
- package/dist/src/core/{transaction.js → install/transaction.js} +1 -1
- package/dist/src/core/{uninstall.js → install/uninstall.js} +4 -4
- package/dist/src/core/{update.js → install/update.js} +4 -4
- package/dist/src/core/{cli-guide.js → reporting/cli-guide.js} +29 -45
- package/dist/src/core/{doctor.js → reporting/doctor.js} +3 -6
- package/dist/src/core/{freshness-alerts.js → reporting/freshness-alerts.js} +5 -5
- package/dist/src/core/{health.js → reporting/health.js} +6 -6
- package/dist/src/core/{loadout-card.js → reporting/loadout-card.js} +1 -1
- package/dist/src/core/{readme-claims.js → reporting/readme-claims.js} +1 -1
- package/dist/src/core/{readme-facts.js → reporting/readme-facts.js} +2 -2
- package/dist/src/core/{share-report.js → reporting/share-report.js} +3 -3
- package/dist/src/core/{upgrade.js → reporting/upgrade.js} +7 -7
- package/dist/src/core/routing/first-party-skills.js +118 -0
- package/dist/src/core/{handoff.js → routing/handoff.js} +120 -3
- package/dist/src/core/{model-config.js → routing/model-config.js} +4 -4
- package/dist/src/core/{route.js → routing/route.js} +325 -34
- package/dist/src/core/{api.js → runtime/api.js} +4 -4
- package/dist/src/core/{canary.js → runtime/canary.js} +1 -1
- package/dist/src/core/{github.js → runtime/github.js} +1 -1
- package/dist/src/core/{mcp-recipes.js → runtime/mcp-recipes.js} +1 -1
- package/dist/src/core/{mcp.js → runtime/mcp.js} +2 -2
- package/dist/src/core/{scheduler.js → runtime/scheduler.js} +4 -4
- package/dist/src/core/{active-policy.js → workspace/active-policy.js} +2 -2
- package/dist/src/core/{active-set.js → workspace/active-set.js} +2 -2
- package/dist/src/core/{improve.js → workspace/improve.js} +3 -3
- package/dist/src/core/{manifest.js → workspace/manifest.js} +2 -2
- package/dist/src/core/{outcomes.js → workspace/outcomes.js} +3 -3
- package/dist/src/core/{portable.js → workspace/portable.js} +3 -3
- package/dist/src/core/{profile-state.js → workspace/profile-state.js} +1 -1
- package/dist/src/core/{recommend.js → workspace/recommend.js} +1 -1
- package/dist/src/core/{state.js → workspace/state.js} +3 -3
- package/docs/DISCOVERED.md +247 -246
- package/docs/evidence/readme-claims.json +22 -19
- package/package.json +2 -1
- package/skills/loadout-curator/SKILL.md +105 -0
- package/skills/loadout-router/SKILL.md +120 -0
- /package/dist/src/core/{agent-health-score.js → agents/agent-health-score.js} +0 -0
- /package/dist/src/core/{agent-versions.js → agents/agent-versions.js} +0 -0
- /package/dist/src/core/{conversion.js → agents/conversion.js} +0 -0
- /package/dist/src/core/{paths.js → agents/paths.js} +0 -0
- /package/dist/src/core/{runtime-tool-recipe.js → agents/runtime-tool-recipe.js} +0 -0
- /package/dist/src/core/{catalog-coverage.js → catalog/catalog-coverage.js} +0 -0
- /package/dist/src/core/{safety.js → catalog/safety.js} +0 -0
- /package/dist/src/core/{skill-security.js → catalog/skill-security.js} +0 -0
- /package/dist/src/core/{community.js → discovery/community.js} +0 -0
- /package/dist/src/core/{github-discovery.js → discovery/github-discovery.js} +0 -0
- /package/dist/src/core/{private-discovery.js → discovery/private-discovery.js} +0 -0
- /package/dist/src/core/{ranking.js → discovery/ranking.js} +0 -0
- /package/dist/src/core/{atomic-file.js → install/atomic-file.js} +0 -0
- /package/dist/src/core/{diff.js → install/diff.js} +0 -0
- /package/dist/src/core/{update-watch.js → install/update-watch.js} +0 -0
- /package/dist/src/core/{completion.js → reporting/completion.js} +0 -0
- /package/dist/src/core/{loadout-badge.js → reporting/loadout-badge.js} +0 -0
- /package/dist/src/core/{terminal.js → reporting/terminal.js} +0 -0
- /package/dist/src/core/{access.js → routing/access.js} +0 -0
- /package/dist/src/core/{credentials.js → routing/credentials.js} +0 -0
- /package/dist/src/core/{sandbox.js → runtime/sandbox.js} +0 -0
- /package/dist/src/core/{active-limit.js → workspace/active-limit.js} +0 -0
- /package/dist/src/core/{target-occupancy.js → workspace/target-occupancy.js} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { parseAgentSelection } from "../core/paths.js";
|
|
2
|
-
import { formatHealthReport } from "../core/health.js";
|
|
3
|
-
import { applyUpgrade, formatUpgradePlan, planUpgrade, summarizeUpgradePlan, } from "../core/upgrade.js";
|
|
4
|
-
import { parseModelApiAccess } from "../core/access.js";
|
|
5
|
-
import { ADVANCED_GUIDE } from "../core/cli-guide.js";
|
|
1
|
+
import { parseAgentSelection } from "../core/agents/paths.js";
|
|
2
|
+
import { formatHealthReport } from "../core/reporting/health.js";
|
|
3
|
+
import { applyUpgrade, formatUpgradePlan, planUpgrade, summarizeUpgradePlan, } from "../core/reporting/upgrade.js";
|
|
4
|
+
import { parseModelApiAccess } from "../core/routing/access.js";
|
|
5
|
+
import { ADVANCED_GUIDE } from "../core/reporting/cli-guide.js";
|
|
6
6
|
import { collectOption, setupSelection, printSetupProgress, runSetup, printBeginnerGuide, } from "./support.js";
|
|
7
7
|
export function registerSetup(program) {
|
|
8
8
|
program
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { loadEffectiveCatalog } from "../core/catalog.js";
|
|
2
|
-
import {
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { loadEffectiveCatalog } from "../core/catalog/catalog.js";
|
|
2
|
+
import { parseAgentSelection } from "../core/agents/paths.js";
|
|
4
3
|
import { resolve } from "node:path";
|
|
5
|
-
import { addManifestPackage, initManifest, readManifest, removeManifestPackage, writeLockfile, } from "../core/manifest.js";
|
|
6
|
-
import { readInstallState } from "../core/state.js";
|
|
7
|
-
import { createPackage,
|
|
8
|
-
import { auditLoadout, formatAuditReport } from "../core/audit.js";
|
|
9
|
-
import { applyPortableImport, exportPortableLoadout, planPortableImport, } from "../core/portable.js";
|
|
10
|
-
import { writeFileAtomically } from "../core/atomic-file.js";
|
|
11
|
-
import { buildLibraryStateReport, formatLibrarySummary, formatLibraryStateReport, } from "../core/active-set.js";
|
|
12
|
-
import { buildPrivacySafeReport, formatPrivacySafeReport,
|
|
13
|
-
import {
|
|
14
|
-
import { createCredentialResolver } from "../core/credentials.js";
|
|
15
|
-
import { buildLoadoutCard, compareLoadoutReports, formatLoadoutCard, formatLoadoutComparison, } from "../core/loadout-card.js";
|
|
4
|
+
import { addManifestPackage, initManifest, readManifest, removeManifestPackage, writeLockfile, } from "../core/workspace/manifest.js";
|
|
5
|
+
import { readInstallState } from "../core/workspace/state.js";
|
|
6
|
+
import { createPackage, searchLocalRegistry, } from "../core/catalog/registry.js";
|
|
7
|
+
import { auditLoadout, formatAuditReport } from "../core/catalog/audit.js";
|
|
8
|
+
import { applyPortableImport, exportPortableLoadout, planPortableImport, } from "../core/workspace/portable.js";
|
|
9
|
+
import { writeFileAtomically } from "../core/install/atomic-file.js";
|
|
10
|
+
import { buildLibraryStateReport, formatLibrarySummary, formatLibraryStateReport, } from "../core/workspace/active-set.js";
|
|
11
|
+
import { buildPrivacySafeReport, formatPrivacySafeReport, writePrivacySafeReport, } from "../core/reporting/share-report.js";
|
|
12
|
+
import { buildLoadoutCard, formatLoadoutCard, } from "../core/reporting/loadout-card.js";
|
|
16
13
|
export function registerSharing(program) {
|
|
17
14
|
program
|
|
18
15
|
.command("init")
|
|
@@ -89,54 +86,6 @@ export function registerSharing(program) {
|
|
|
89
86
|
const descriptor = await createPackage(directory, options);
|
|
90
87
|
console.log(`Created ${descriptor.name}@${descriptor.version} in ${directory}.`);
|
|
91
88
|
});
|
|
92
|
-
program
|
|
93
|
-
.command("pack")
|
|
94
|
-
.description("Validate a package and print its deterministic inventory digest")
|
|
95
|
-
.argument("[directory]", "package directory", ".")
|
|
96
|
-
.option("--json", "emit machine-readable JSON")
|
|
97
|
-
.action(async (directory, options) => {
|
|
98
|
-
const packed = await packPackage(directory);
|
|
99
|
-
console.log(options.json
|
|
100
|
-
? JSON.stringify(packed, null, 2)
|
|
101
|
-
: `${packed.descriptor.name}@${packed.descriptor.version} — ${packed.files.length} file(s) — sha256:${packed.digest}`);
|
|
102
|
-
});
|
|
103
|
-
program
|
|
104
|
-
.command("publish")
|
|
105
|
-
.description("Publish an immutable package version to a local or remote Loadout registry")
|
|
106
|
-
.argument("[directory]", "package directory", ".")
|
|
107
|
-
.option("--local", "publish to the local registry")
|
|
108
|
-
.option("--registry-url <url>", "remote registry base URL")
|
|
109
|
-
.option("--credential-keychain <service>", "resolve the remote registry token from the OS credential store")
|
|
110
|
-
.option("--credential-account <account>", "OS credential account")
|
|
111
|
-
.option("--approve-risk", "explicitly approve publishing scripts, hooks, or binaries")
|
|
112
|
-
.action(async (directory, options) => {
|
|
113
|
-
if (Number(Boolean(options.local)) +
|
|
114
|
-
Number(Boolean(options.registryUrl)) !==
|
|
115
|
-
1)
|
|
116
|
-
throw new Error("Choose exactly one destination: --local or --registry-url");
|
|
117
|
-
if (options.local) {
|
|
118
|
-
if (options.credentialKeychain)
|
|
119
|
-
throw new Error("Local publishing does not require a credential");
|
|
120
|
-
const packed = await publishLocalPackage(directory, {
|
|
121
|
-
approveRisk: options.approveRisk,
|
|
122
|
-
});
|
|
123
|
-
console.log(`Published ${packed.descriptor.name}@${packed.descriptor.version} with digest ${packed.digest}.`);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
const token = await createCredentialResolver()(options.credentialKeychain
|
|
127
|
-
? {
|
|
128
|
-
kind: "os-keychain",
|
|
129
|
-
service: options.credentialKeychain,
|
|
130
|
-
...(options.credentialAccount
|
|
131
|
-
? { account: options.credentialAccount }
|
|
132
|
-
: {}),
|
|
133
|
-
}
|
|
134
|
-
: { kind: "environment", name: "LOADOUT_REGISTRY_TOKEN" });
|
|
135
|
-
if (!token)
|
|
136
|
-
throw new Error("Remote publishing credential did not resolve; set LOADOUT_REGISTRY_TOKEN or use --credential-keychain");
|
|
137
|
-
const published = await publishRemotePackage(directory, options.registryUrl, token, { approveRisk: options.approveRisk });
|
|
138
|
-
console.log(`Published ${published.name}@${published.version} with digest ${published.digest}.`);
|
|
139
|
-
});
|
|
140
89
|
program
|
|
141
90
|
.command("search")
|
|
142
91
|
.description("Search the bundled catalog and local registry")
|
|
@@ -283,39 +232,6 @@ export function registerSharing(program) {
|
|
|
283
232
|
? JSON.stringify(report, null, 2)
|
|
284
233
|
: formatPrivacySafeReport(report));
|
|
285
234
|
});
|
|
286
|
-
program
|
|
287
|
-
.command("outcomes")
|
|
288
|
-
.description("Show privacy-safe local outcome signals; never uploads project or prompt data")
|
|
289
|
-
.option("--json", "emit machine-readable JSON")
|
|
290
|
-
.action(async (options) => {
|
|
291
|
-
const store = await readLocalOutcomes();
|
|
292
|
-
console.log(options.json
|
|
293
|
-
? JSON.stringify(store, null, 2)
|
|
294
|
-
: [
|
|
295
|
-
`Local outcomes: ${store.events.length}`,
|
|
296
|
-
...store.events.map((event) => `${event.recordedAt} — ${event.selector} — ${event.agent}/${event.taskFamily} — ${event.result}`),
|
|
297
|
-
"Privacy: local only; no project names, paths, prompts, code, filenames, or secrets.",
|
|
298
|
-
].join("\n"));
|
|
299
|
-
});
|
|
300
|
-
program
|
|
301
|
-
.command("outcome")
|
|
302
|
-
.description("Record an explicit local, agent/task-scoped skill outcome")
|
|
303
|
-
.argument("<selector>", "exact package/skill selector")
|
|
304
|
-
.requiredOption("--agent <id>", "agent id")
|
|
305
|
-
.requiredOption("--task <family>", "general, frontend, testing, javascript, python, backend, security, or documentation")
|
|
306
|
-
.requiredOption("--result <value>", "accept, reject, success, failure, activation, disable, or rollback")
|
|
307
|
-
.action(async (selector, options) => {
|
|
308
|
-
const knownAgents = new Set((await detectAgents()).map((agent) => agent.id));
|
|
309
|
-
if (!knownAgents.has(options.agent))
|
|
310
|
-
throw new Error(`Unknown agent id: ${options.agent}`);
|
|
311
|
-
const event = await recordLocalOutcome({
|
|
312
|
-
selector,
|
|
313
|
-
agent: options.agent,
|
|
314
|
-
taskFamily: options.task,
|
|
315
|
-
result: options.result,
|
|
316
|
-
});
|
|
317
|
-
console.log(`Recorded local outcome ${event.id}. No project, prompt, or source data was stored.`);
|
|
318
|
-
});
|
|
319
235
|
program
|
|
320
236
|
.command("share")
|
|
321
237
|
.description("Write the privacy-safe Loadout report to a JSON artifact")
|
|
@@ -342,17 +258,4 @@ export function registerSharing(program) {
|
|
|
342
258
|
}
|
|
343
259
|
console.log(options.json ? JSON.stringify(card, null, 2) : markdown);
|
|
344
260
|
});
|
|
345
|
-
program
|
|
346
|
-
.command("compare-loadouts")
|
|
347
|
-
.description("Compare aggregate counts from two explicit privacy-safe Loadout reports")
|
|
348
|
-
.argument("<left>", "earlier privacy-safe report JSON")
|
|
349
|
-
.argument("<right>", "later privacy-safe report JSON")
|
|
350
|
-
.option("--json", "emit machine-readable count deltas")
|
|
351
|
-
.action(async (left, right, options) => {
|
|
352
|
-
const [before, after] = await Promise.all([left, right].map(async (path) => parsePrivacySafeLoadoutReport(JSON.parse(await readFile(resolve(path), "utf8")))));
|
|
353
|
-
const comparison = compareLoadoutReports(before, after);
|
|
354
|
-
console.log(options.json
|
|
355
|
-
? JSON.stringify(comparison, null, 2)
|
|
356
|
-
: formatLoadoutComparison(comparison));
|
|
357
|
-
});
|
|
358
261
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
-
import { detectAgents, parseAgentSelection } from "../core/paths.js";
|
|
3
|
+
import { detectAgents, parseAgentSelection } from "../core/agents/paths.js";
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { applyPreparedCatalogInstall, formatCatalogApplyGuidance, formatPreparedCatalogInstall, prepareCatalogInstall, } from "../core/catalog-install.js";
|
|
7
|
-
import { formatInstalledSkillInventory, scanInstalledSkills, } from "../core/skill-inventory.js";
|
|
8
|
-
import { interactiveModelApiAccess, parseModelApiAccess, } from "../core/access.js";
|
|
9
|
-
import { BEGINNER_GUIDE } from "../core/cli-guide.js";
|
|
6
|
+
import { applyPreparedCatalogInstall, formatCatalogApplyGuidance, formatPreparedCatalogInstall, prepareCatalogInstall, } from "../core/install/catalog-install.js";
|
|
7
|
+
import { formatInstalledSkillInventory, scanInstalledSkills, } from "../core/catalog/skill-inventory.js";
|
|
8
|
+
import { interactiveModelApiAccess, parseModelApiAccess, } from "../core/routing/access.js";
|
|
9
|
+
import { BEGINNER_GUIDE } from "../core/reporting/cli-guide.js";
|
|
10
10
|
export const collectOption = (value, previous = []) => [...previous, value];
|
|
11
11
|
export function parseMcpCredentialMappings(recipe, mappings, account) {
|
|
12
12
|
const references = {};
|
|
@@ -105,7 +105,16 @@ export async function runSetup(options) {
|
|
|
105
105
|
try {
|
|
106
106
|
if (!mode) {
|
|
107
107
|
if (!interactive) {
|
|
108
|
-
console.log(
|
|
108
|
+
console.log([
|
|
109
|
+
"Pick a loadout to preview:",
|
|
110
|
+
" --mode stable 4 reviewed sources, 30 skills, all active (recommended)",
|
|
111
|
+
" --mode power 8 sources, 56 skills, all active",
|
|
112
|
+
" --mode maximum every reviewed source, downloaded but DISABLED; a project",
|
|
113
|
+
" activates what it needs with `loadout optimize --project .`",
|
|
114
|
+
" --mode custom --package <id>, one or more",
|
|
115
|
+
"",
|
|
116
|
+
"Nothing installs until you add --yes.",
|
|
117
|
+
].join("\n"));
|
|
109
118
|
return;
|
|
110
119
|
}
|
|
111
120
|
reader = createInterface({
|
|
@@ -114,9 +123,11 @@ export async function runSetup(options) {
|
|
|
114
123
|
});
|
|
115
124
|
const answer = (await reader.question([
|
|
116
125
|
"Choose a loadout:",
|
|
117
|
-
" [1] Stable —
|
|
118
|
-
" [2] Power —
|
|
119
|
-
" [3] Maximum — download
|
|
126
|
+
" [1] Stable — 4 reviewed sources, 30 skills, all active (recommended)",
|
|
127
|
+
" [2] Power — 8 sources, 56 skills, all active",
|
|
128
|
+
" [3] Maximum — download every reviewed source, but leave it DISABLED.",
|
|
129
|
+
" Nothing reaches your prompts until a project activates",
|
|
130
|
+
" it with `loadout optimize --project .`. Costs disk, not context.",
|
|
120
131
|
" [4] Custom — pick exact package ids",
|
|
121
132
|
"> ",
|
|
122
133
|
].join("\n"))).trim();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
|
-
import { planSkillInstall } from "
|
|
3
|
-
import { formatTerminalTable } from "
|
|
2
|
+
import { planSkillInstall } from "../catalog/skills.js";
|
|
3
|
+
import { formatTerminalTable } from "../reporting/terminal.js";
|
|
4
4
|
/**
|
|
5
5
|
* Rules shared by the CLI, planner, and documentation. A capability only
|
|
6
6
|
* changes state when it is native or has this explicitly tested adaptation.
|
|
@@ -136,15 +136,35 @@ export async function inspectAgent(agent) {
|
|
|
136
136
|
export async function inspectAgents(agents) {
|
|
137
137
|
return Promise.all(agents.map(inspectAgent));
|
|
138
138
|
}
|
|
139
|
-
|
|
139
|
+
/**
|
|
140
|
+
* One agent's inventory. The default is a two-line summary: what is installed
|
|
141
|
+
* and which component kinds the adapter supports. Adapter internals — the
|
|
142
|
+
* per-kind compatibility notes and the paths that do not exist yet — are only
|
|
143
|
+
* useful when diagnosing, so they stay behind `details`.
|
|
144
|
+
*/
|
|
145
|
+
export function formatAgentInventory(inventory, options = {}) {
|
|
140
146
|
const lines = [
|
|
141
147
|
`${inventory.agent.installed ? "✓" : "○"} ${inventory.agent.displayName}`,
|
|
142
148
|
];
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
if (options.details) {
|
|
150
|
+
for (const component of inventory.components) {
|
|
151
|
+
const state = component.scanned
|
|
152
|
+
? `${component.directoryExists ? `${component.entries.length} filesystem item(s)` : "not created"} — ${component.directory}`
|
|
153
|
+
: (component.note ?? "not inspected");
|
|
154
|
+
lines.push(` ${component.type}: ${component.compatibility}; ${state}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
const items = inventory.components
|
|
159
|
+
.filter((component) => component.scanned && component.directoryExists)
|
|
160
|
+
.reduce((total, component) => total + component.entries.length, 0);
|
|
161
|
+
const supported = inventory.components
|
|
162
|
+
.filter((component) => component.compatibility !== "unsupported")
|
|
163
|
+
.map((component) => component.type);
|
|
164
|
+
const skills = inventory.components.find((component) => component.type === "skill");
|
|
165
|
+
if (skills?.directory)
|
|
166
|
+
lines.push(` ${skills.directory}`);
|
|
167
|
+
lines.push(` ${items} items | supports: ${supported.join(", ")}`);
|
|
148
168
|
}
|
|
149
169
|
for (const warning of inventory.warnings)
|
|
150
170
|
lines.push(` ! ${warning}`);
|
|
@@ -2,7 +2,7 @@ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { basename, dirname, join, resolve } from "node:path";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import { loadoutHome, userHome } from "./paths.js";
|
|
5
|
-
import { runMutationTransaction } from "
|
|
5
|
+
import { runMutationTransaction } from "../install/transaction.js";
|
|
6
6
|
function safeName(name) {
|
|
7
7
|
if (!/^[A-Za-z0-9._-]{1,128}$/.test(name))
|
|
8
8
|
throw new Error("MCP server name may contain only letters, numbers, ., _, and -");
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import { buildAgentHealthScore, } from "./agent-health-score.js";
|
|
4
|
-
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "
|
|
5
|
-
import { managedFileReadPath } from "
|
|
4
|
+
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "../workspace/active-limit.js";
|
|
5
|
+
import { managedFileReadPath } from "../workspace/active-set.js";
|
|
6
6
|
import { adapterCapabilities } from "./adapters.js";
|
|
7
|
-
import { loadEffectiveCatalog } from "
|
|
7
|
+
import { loadEffectiveCatalog } from "../catalog/catalog.js";
|
|
8
8
|
import { detectAgents } from "./paths.js";
|
|
9
|
-
import { readLocalOutcomes } from "
|
|
10
|
-
import { readSnapshot } from "
|
|
11
|
-
import { scanInstalledSkills } from "
|
|
12
|
-
import { readInstallState } from "
|
|
13
|
-
import { transactionRoot } from "
|
|
9
|
+
import { readLocalOutcomes } from "../workspace/outcomes.js";
|
|
10
|
+
import { readSnapshot } from "../install/snapshot.js";
|
|
11
|
+
import { scanInstalledSkills } from "../catalog/skill-inventory.js";
|
|
12
|
+
import { readInstallState } from "../workspace/state.js";
|
|
13
|
+
import { transactionRoot } from "../install/transaction.js";
|
|
14
14
|
function freshness(pkg, asOf) {
|
|
15
15
|
const observed = pkg?.pushedAt ?? pkg?.lastUpdatedAt;
|
|
16
16
|
if (!observed)
|
|
@@ -2,10 +2,10 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import { access, readFile, readdir, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
-
import { writeFileAtomically } from "
|
|
5
|
+
import { writeFileAtomically } from "../install/atomic-file.js";
|
|
6
6
|
import { detectAgents, loadoutHome, userHome } from "./paths.js";
|
|
7
7
|
import { parseRuntimeToolRecipe, renderRuntimeRecipeValue, resolveRuntimeRecipePath, } from "./runtime-tool-recipe.js";
|
|
8
|
-
import { createSnapshot, readSnapshot, recordSnapshotPostMutationState, restoreSnapshot, } from "
|
|
8
|
+
import { createSnapshot, readSnapshot, recordSnapshotPostMutationState, restoreSnapshot, } from "../install/snapshot.js";
|
|
9
9
|
const execFileAsync = promisify(execFile);
|
|
10
10
|
function deepFreeze(value) {
|
|
11
11
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import { formatSchemaError, loadoutLockfileSchema } from "
|
|
5
|
-
import { readManifest } from "
|
|
6
|
-
import { readInstallState } from "
|
|
7
|
-
import { managedFileReadPath } from "
|
|
4
|
+
import { formatSchemaError, loadoutLockfileSchema, } from "../../shared/schemas.js";
|
|
5
|
+
import { readManifest } from "../workspace/manifest.js";
|
|
6
|
+
import { readInstallState } from "../workspace/state.js";
|
|
7
|
+
import { managedFileReadPath } from "../workspace/active-set.js";
|
|
8
8
|
export function parseLockfile(value) {
|
|
9
9
|
const result = loadoutLockfileSchema.safeParse(value);
|
|
10
10
|
if (!result.success)
|
|
@@ -2,12 +2,12 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { catalogSchema, formatSchemaError } from "
|
|
6
|
-
import { fetchGitHubMetadata } from "
|
|
7
|
-
import { loadoutHome } from "
|
|
8
|
-
import { compareCatalogPackages, explainCatalogScore } from "
|
|
5
|
+
import { catalogSchema, formatSchemaError } from "../../shared/schemas.js";
|
|
6
|
+
import { fetchGitHubMetadata, } from "../runtime/github.js";
|
|
7
|
+
import { loadoutHome } from "../agents/paths.js";
|
|
8
|
+
import { compareCatalogPackages, explainCatalogScore, } from "../discovery/ranking.js";
|
|
9
9
|
import { resolveCatalogProfile } from "./profiles.js";
|
|
10
|
-
import { recordCatalogObservations } from "
|
|
10
|
+
import { recordCatalogObservations } from "../discovery/observations.js";
|
|
11
11
|
const cachedCatalogPath = () => join(loadoutHome(), "catalog.json");
|
|
12
12
|
function bundledCatalogPath() {
|
|
13
13
|
const moduleDirectory = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { lstat, readdir } from "node:fs/promises";
|
|
2
2
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
-
import { buildSkillPlan } from "
|
|
4
|
-
import { adapterCapabilities, agentComponentDirectory } from "
|
|
3
|
+
import { buildSkillPlan } from "../install/install.js";
|
|
4
|
+
import { adapterCapabilities, agentComponentDirectory, } from "../agents/adapters.js";
|
|
5
5
|
const RESOURCE_DIRECTORIES = new Map([
|
|
6
6
|
["rules", "rule"],
|
|
7
7
|
["commands", "command"],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ADAPTER_CAPABILITIES, agentComponentDirectory } from "
|
|
2
|
-
import { AGENT_DEFINITIONS, agentSkillsDirectory } from "
|
|
1
|
+
import { ADAPTER_CAPABILITIES, agentComponentDirectory, } from "../agents/adapters.js";
|
|
2
|
+
import { AGENT_DEFINITIONS, agentSkillsDirectory } from "../agents/paths.js";
|
|
3
3
|
const PLATFORM_BY_RUNNER = {
|
|
4
4
|
"ubuntu-latest": "linux",
|
|
5
5
|
"macos-latest": "macos",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compareCatalogPackages } from "
|
|
1
|
+
import { compareCatalogPackages } from "../discovery/ranking.js";
|
|
2
2
|
/**
|
|
3
3
|
* Stable is Loadout's bounded policy-selected daily driver: broad enough to improve normal
|
|
4
4
|
* engineering work immediately, but bounded at skill granularity and restricted
|
|
@@ -247,7 +247,7 @@ export function resolveCatalogProfile(packages, selection, families = CATALOG_CO
|
|
|
247
247
|
warnings.push(`Custom selection retains the soft overlap in ${family.label}. Review ${names} before installation.`);
|
|
248
248
|
}
|
|
249
249
|
else {
|
|
250
|
-
warnings.push(`${selection.mode === "power" ? "Power
|
|
250
|
+
warnings.push(`${selection.mode === "power" ? "Power" : "Maximum"} retains the soft overlap in ${family.label}. Loadout policy orders ${primary.displayName} first; review each package before installation.`);
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
return {
|
|
@@ -2,10 +2,10 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { loadEffectiveCatalog } from "./catalog.js";
|
|
5
|
-
import { ensureDirectory, loadoutHome } from "
|
|
5
|
+
import { ensureDirectory, loadoutHome } from "../agents/paths.js";
|
|
6
6
|
import { discoverSkillDirectories } from "./skills.js";
|
|
7
|
-
import { fetchRepositorySnapshot, } from "
|
|
8
|
-
import { writeFileAtomically } from "
|
|
7
|
+
import { fetchRepositorySnapshot, } from "../install/source.js";
|
|
8
|
+
import { writeFileAtomically } from "../install/atomic-file.js";
|
|
9
9
|
const catalogSkillIndexPath = () => join(loadoutHome(), "provenance", "catalog-skills.json");
|
|
10
10
|
function immutableCatalogDigest(catalog) {
|
|
11
11
|
const evidence = catalog
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { cp, lstat, mkdir, readFile, readdir, rm, writeFile, } from "node:fs/promises";
|
|
3
3
|
import { join, relative, resolve, sep } from "node:path";
|
|
4
|
-
import { loadoutHome } from "
|
|
4
|
+
import { loadoutHome } from "../agents/paths.js";
|
|
5
5
|
import { analyzeUpdateSafety } from "./safety.js";
|
|
6
6
|
const NAME = /^[a-z0-9][a-z0-9._-]*$/;
|
|
7
7
|
const VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?$/;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { lstat, readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import { join, relative, resolve, sep } from "node:path";
|
|
4
|
-
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "
|
|
5
|
-
import { readInstallState } from "
|
|
6
|
-
import { listInstalledRuntimeToolSkillTargets, } from "
|
|
4
|
+
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "../workspace/active-limit.js";
|
|
5
|
+
import { readInstallState } from "../workspace/state.js";
|
|
6
|
+
import { listInstalledRuntimeToolSkillTargets, } from "../agents/runtime-tools.js";
|
|
7
7
|
const MAX_SCAN_DEPTH = 6;
|
|
8
8
|
function cleanFrontmatterValue(value) {
|
|
9
9
|
if (!value)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cp, lstat, readdir, readFile, rm } from "node:fs/promises";
|
|
2
2
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
3
|
-
import { ensureDirectory } from "
|
|
3
|
+
import { ensureDirectory } from "../agents/paths.js";
|
|
4
4
|
import { assertSkillSecurity, scanSkillSecurity } from "./skill-security.js";
|
|
5
5
|
export async function discoverSkillDirectories(root, options = {}) {
|
|
6
6
|
const result = [];
|
|
@@ -4,13 +4,13 @@ import { readFile, stat } from "node:fs/promises";
|
|
|
4
4
|
import { dirname, join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { isDeepStrictEqual } from "node:util";
|
|
7
|
-
import { writeFileAtomically } from "
|
|
8
|
-
import { loadEffectiveCatalog, validateCatalog } from "
|
|
7
|
+
import { writeFileAtomically } from "../install/atomic-file.js";
|
|
8
|
+
import { loadEffectiveCatalog, validateCatalog } from "../catalog/catalog.js";
|
|
9
9
|
import { evaluatePackage } from "./evaluate.js";
|
|
10
|
-
import { inspectPackage } from "
|
|
11
|
-
import { ensureDirectory, loadoutHome } from "
|
|
12
|
-
import { fetchRepositorySnapshot, normalizeRepository, } from "
|
|
13
|
-
import { safeTerminalText } from "
|
|
10
|
+
import { inspectPackage } from "../install/package.js";
|
|
11
|
+
import { ensureDirectory, loadoutHome } from "../agents/paths.js";
|
|
12
|
+
import { fetchRepositorySnapshot, normalizeRepository, } from "../install/source.js";
|
|
13
|
+
import { safeTerminalText } from "../reporting/terminal.js";
|
|
14
14
|
const dossierDirectory = () => join(loadoutHome(), "candidates");
|
|
15
15
|
const sourceVerifiedDossiers = new WeakMap();
|
|
16
16
|
function installabilityFor(components) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
import { writeFileAtomically } from "
|
|
4
|
-
import { ensureDirectory } from "
|
|
3
|
+
import { writeFileAtomically } from "../install/atomic-file.js";
|
|
4
|
+
import { ensureDirectory } from "../agents/paths.js";
|
|
5
5
|
export class ConnectorRequestError extends Error {
|
|
6
6
|
status;
|
|
7
7
|
retryAfterSeconds;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { inspectPackage } from "
|
|
2
|
-
import { analyzeUpdateSafety } from "
|
|
1
|
+
import { inspectPackage } from "../install/package.js";
|
|
2
|
+
import { analyzeUpdateSafety } from "../catalog/safety.js";
|
|
3
3
|
function statusFor(findings) {
|
|
4
4
|
if (findings.some((finding) => finding.category === "secret" || finding.category === "instruction"))
|
|
5
5
|
return "blocked";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { ConnectorRequestError, fetchBoundedJson, rateLimitFromHeaders, readDiscoveryConnectorCache, writeDiscoveryConnectorCache, } from "./discovery-connector.js";
|
|
3
|
-
import { loadoutHome } from "
|
|
3
|
+
import { loadoutHome } from "../agents/paths.js";
|
|
4
4
|
const SOURCE = "official-mcp-registry";
|
|
5
5
|
const API = "https://registry.modelcontextprotocol.io/v0.1/servers";
|
|
6
6
|
const ATTRIBUTION = "https://registry.modelcontextprotocol.io/docs";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { fetchGitHubReleaseMetadata, } from "
|
|
4
|
-
import { writeFileAtomically } from "
|
|
5
|
-
import { loadoutHome } from "
|
|
3
|
+
import { fetchGitHubReleaseMetadata, } from "../runtime/github.js";
|
|
4
|
+
import { writeFileAtomically } from "../install/atomic-file.js";
|
|
5
|
+
import { loadoutHome } from "../agents/paths.js";
|
|
6
6
|
const MAX_OBSERVATIONS_PER_REPOSITORY = 90;
|
|
7
7
|
function observationPath(repository) {
|
|
8
8
|
return join(loadoutHome(), "observations", `${repository.replace("/", "__")}.json`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { writeFileAtomically } from "
|
|
4
|
-
import { ensureDirectory, loadoutHome } from "
|
|
3
|
+
import { writeFileAtomically } from "../install/atomic-file.js";
|
|
4
|
+
import { ensureDirectory, loadoutHome } from "../agents/paths.js";
|
|
5
5
|
const queuePath = () => join(loadoutHome(), "review-queue.json");
|
|
6
6
|
function isQueue(value) {
|
|
7
7
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { ConnectorRequestError, fetchBoundedJson, rateLimitFromHeaders, readDiscoveryConnectorCache, writeDiscoveryConnectorCache, } from "./discovery-connector.js";
|
|
3
|
-
import { loadoutHome } from "
|
|
3
|
+
import { loadoutHome } from "../agents/paths.js";
|
|
4
4
|
const SOURCE = "skills-sh";
|
|
5
5
|
const API = "https://skills.sh/api/v1/skills";
|
|
6
6
|
const ATTRIBUTION = "https://skills.sh/docs/api";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { lstat, readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import { basename, isAbsolute, join, posix, relative, sep } from "node:path";
|
|
4
|
-
import { enrichInventoryWithProvenance } from "
|
|
5
|
-
import { scanInstalledSkills } from "
|
|
6
|
-
import { installStatePath, recordInstall } from "
|
|
4
|
+
import { enrichInventoryWithProvenance } from "../catalog/provenance.js";
|
|
5
|
+
import { scanInstalledSkills } from "../catalog/skill-inventory.js";
|
|
6
|
+
import { installStatePath, recordInstall } from "../workspace/state.js";
|
|
7
7
|
import { runMutationTransaction } from "./transaction.js";
|
|
8
8
|
const issuedAdoptionPlans = new WeakMap();
|
|
9
9
|
function safeRelativePath(root, path) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "
|
|
2
|
-
import { loadEffectiveCatalog } from "
|
|
3
|
-
import { detectAgents } from "
|
|
4
|
-
import { isStableSkillSelected, isPowerSkillSelected, resolveCatalogProfile, } from "
|
|
1
|
+
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "../workspace/active-limit.js";
|
|
2
|
+
import { loadEffectiveCatalog, } from "../catalog/catalog.js";
|
|
3
|
+
import { detectAgents } from "../agents/paths.js";
|
|
4
|
+
import { isStableSkillSelected, isPowerSkillSelected, resolveCatalogProfile, } from "../catalog/profiles.js";
|
|
5
5
|
import { applySkillLibraryBatch, applySkillInstallBatch, buildSkillPlan, installedAgents, planManagedProfileReconciliation, } from "./install.js";
|
|
6
6
|
import { fetchRepositorySnapshot, } from "./source.js";
|
|
7
|
-
import { analyzeInstallPlanSafety, } from "
|
|
8
|
-
import { formatModelApiAccess } from "
|
|
9
|
-
import { recordInstalledProfile } from "
|
|
10
|
-
import { readInstallState } from "
|
|
7
|
+
import { analyzeInstallPlanSafety, } from "../catalog/safety.js";
|
|
8
|
+
import { formatModelApiAccess, } from "../routing/access.js";
|
|
9
|
+
import { recordInstalledProfile } from "../workspace/profile-state.js";
|
|
10
|
+
import { readInstallState } from "../workspace/state.js";
|
|
11
11
|
async function parallelMap(values, concurrency, worker) {
|
|
12
12
|
const results = new Array(values.length);
|
|
13
13
|
let cursor = 0;
|
|
@@ -240,11 +240,11 @@ export function formatPreparedCatalogInstall(prepared, options = {}) {
|
|
|
240
240
|
const quarantined = prepared.skipped.filter((item) => item.kind === "quarantined");
|
|
241
241
|
const retired = prepared.reconciliation?.obsoleteUnits ?? [];
|
|
242
242
|
const modeLabel = prepared.selection.mode === "maximum"
|
|
243
|
-
? "Maximum
|
|
243
|
+
? "Maximum"
|
|
244
244
|
: prepared.selection.mode === "power"
|
|
245
|
-
? "Power
|
|
245
|
+
? "Power"
|
|
246
246
|
: prepared.selection.mode === "stable"
|
|
247
|
-
? "Stable
|
|
247
|
+
? "Stable"
|
|
248
248
|
: "Custom";
|
|
249
249
|
const lines = [
|
|
250
250
|
`Loadout: ${modeLabel}`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { mkdir, open, readFile, rename, rm, stat, utimes, } from "node:fs/promises";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
|
-
import { ensureDirectory } from "
|
|
4
|
+
import { ensureDirectory } from "../agents/paths.js";
|
|
5
5
|
const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
6
6
|
function parseOwner(content) {
|
|
7
7
|
try {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { cp, lstat, rm } from "node:fs/promises";
|
|
3
3
|
import { basename, dirname, isAbsolute, join, posix, relative, win32, } from "node:path";
|
|
4
|
-
import { ensureDirectory, loadoutHome } from "
|
|
5
|
-
import { planAdapterSkillInstall } from "
|
|
6
|
-
import { applySkillPlan, detectInstallConflicts } from "
|
|
7
|
-
import { activationLibraryPath, installStatePath, recordInstall, recordInstallBatch, recordLibraryInstallBatch, readInstallState, hashDirectory, writeInstallState, } from "
|
|
4
|
+
import { ensureDirectory, loadoutHome } from "../agents/paths.js";
|
|
5
|
+
import { planAdapterSkillInstall } from "../agents/adapters.js";
|
|
6
|
+
import { applySkillPlan, detectInstallConflicts } from "../catalog/skills.js";
|
|
7
|
+
import { activationLibraryPath, installStatePath, recordInstall, recordInstallBatch, recordLibraryInstallBatch, readInstallState, hashDirectory, writeInstallState, } from "../workspace/state.js";
|
|
8
8
|
import { runMutationTransaction } from "./transaction.js";
|
|
9
|
-
import { inspectTargetOccupancy } from "
|
|
9
|
+
import { inspectTargetOccupancy } from "../workspace/target-occupancy.js";
|
|
10
10
|
export function installedAgents(agents, requested) {
|
|
11
11
|
const available = agents.filter((agent) => agent.installed);
|
|
12
12
|
if (!requested || requested.length === 0)
|