loadout-ai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/MASTER_PLAN.md +1711 -0
- package/README.md +453 -0
- package/SECURITY.md +18 -0
- package/SIMPLE_PLAN.md +44 -0
- package/catalog/discovered.json +10469 -0
- package/catalog/packages.json +1150 -0
- package/dashboard/app.js +607 -0
- package/dashboard/index.html +249 -0
- package/dashboard/styles.css +384 -0
- package/dist/src/cli.js +2735 -0
- package/dist/src/core/active-policy.js +233 -0
- package/dist/src/core/active-set.js +382 -0
- package/dist/src/core/adapters.js +254 -0
- package/dist/src/core/adopt.js +92 -0
- package/dist/src/core/agent-health-score.js +475 -0
- package/dist/src/core/agent-inspection.js +152 -0
- package/dist/src/core/agent-versions.js +131 -0
- package/dist/src/core/api.js +90 -0
- package/dist/src/core/atomic-file.js +22 -0
- package/dist/src/core/audit.js +174 -0
- package/dist/src/core/benchmark-campaign.js +496 -0
- package/dist/src/core/benchmark-evidence.js +458 -0
- package/dist/src/core/benchmark-fixtures.js +1157 -0
- package/dist/src/core/benchmark-runner.js +301 -0
- package/dist/src/core/benchmark-trust.js +795 -0
- package/dist/src/core/canary.js +77 -0
- package/dist/src/core/candidate-intelligence.js +661 -0
- package/dist/src/core/catalog-coverage.js +85 -0
- package/dist/src/core/catalog-install.js +186 -0
- package/dist/src/core/catalog-release.js +286 -0
- package/dist/src/core/catalog.js +327 -0
- package/dist/src/core/codex-mcp.js +122 -0
- package/dist/src/core/community.js +86 -0
- package/dist/src/core/compatibility-intelligence.js +465 -0
- package/dist/src/core/completion.js +160 -0
- package/dist/src/core/components.js +138 -0
- package/dist/src/core/conversion.js +56 -0
- package/dist/src/core/credentials.js +377 -0
- package/dist/src/core/demo.js +136 -0
- package/dist/src/core/diff.js +83 -0
- package/dist/src/core/discovery-connector.js +165 -0
- package/dist/src/core/doctor.js +95 -0
- package/dist/src/core/ecosystem-import.js +1072 -0
- package/dist/src/core/evaluate.js +72 -0
- package/dist/src/core/file-lock.js +140 -0
- package/dist/src/core/freshness-alerts.js +230 -0
- package/dist/src/core/github-discovery.js +97 -0
- package/dist/src/core/github.js +148 -0
- package/dist/src/core/head-to-head.js +437 -0
- package/dist/src/core/health-score-evidence.js +169 -0
- package/dist/src/core/health.js +129 -0
- package/dist/src/core/improve.js +223 -0
- package/dist/src/core/install.js +233 -0
- package/dist/src/core/intelligence-feed-build.js +59 -0
- package/dist/src/core/intelligence-feed.js +353 -0
- package/dist/src/core/loadout-badge.js +55 -0
- package/dist/src/core/loadout-card.js +87 -0
- package/dist/src/core/manifest.js +307 -0
- package/dist/src/core/mcp-recipes.js +419 -0
- package/dist/src/core/mcp-registry-discovery.js +455 -0
- package/dist/src/core/mcp.js +287 -0
- package/dist/src/core/model-config.js +102 -0
- package/dist/src/core/observations.js +87 -0
- package/dist/src/core/outcomes.js +164 -0
- package/dist/src/core/package.js +225 -0
- package/dist/src/core/paths.js +214 -0
- package/dist/src/core/portable.js +130 -0
- package/dist/src/core/private-discovery.js +45 -0
- package/dist/src/core/profiles.js +255 -0
- package/dist/src/core/provenance.js +278 -0
- package/dist/src/core/ranking.js +104 -0
- package/dist/src/core/recommend.js +181 -0
- package/dist/src/core/registry-api.js +97 -0
- package/dist/src/core/registry.js +316 -0
- package/dist/src/core/release-claims.js +175 -0
- package/dist/src/core/remove.js +122 -0
- package/dist/src/core/review-queue.js +200 -0
- package/dist/src/core/runtime-tool-recipe.js +315 -0
- package/dist/src/core/runtime-tools.js +453 -0
- package/dist/src/core/safety.js +234 -0
- package/dist/src/core/sandbox.js +78 -0
- package/dist/src/core/scheduler.js +264 -0
- package/dist/src/core/share-report.js +85 -0
- package/dist/src/core/signing.js +115 -0
- package/dist/src/core/skill-compare.js +239 -0
- package/dist/src/core/skill-inventory.js +193 -0
- package/dist/src/core/skill-security.js +340 -0
- package/dist/src/core/skills-sh-discovery.js +336 -0
- package/dist/src/core/skills.js +208 -0
- package/dist/src/core/snapshot.js +210 -0
- package/dist/src/core/source.js +305 -0
- package/dist/src/core/state.js +300 -0
- package/dist/src/core/sync.js +348 -0
- package/dist/src/core/terminal.js +53 -0
- package/dist/src/core/transaction.js +211 -0
- package/dist/src/core/update-watch.js +39 -0
- package/dist/src/core/update.js +216 -0
- package/dist/src/core/upgrade.js +207 -0
- package/dist/src/dashboard.js +418 -0
- package/dist/src/shared/schemas.js +336 -0
- package/dist/src/shared/types.js +1 -0
- package/docs/ACTIVE_SET.md +53 -0
- package/docs/ACTIVE_SET_POLICY.md +45 -0
- package/docs/CANDIDATE_INTELLIGENCE.md +139 -0
- package/docs/CATALOG.md +78 -0
- package/docs/CATALOG_POLICY.md +41 -0
- package/docs/COMMUNITY_DISCOVERY.md +14 -0
- package/docs/COMPATIBILITY_POLICY.md +22 -0
- package/docs/CONVERSION_AND_SANDBOX.md +27 -0
- package/docs/CREDENTIAL_AND_UPDATE_POLICY.md +31 -0
- package/docs/DISCOVERED.md +280 -0
- package/docs/EVALUATION_PROTOCOL.md +40 -0
- package/docs/EVALUATION_PROTOCOL_V1.md +300 -0
- package/docs/FEATURE_TEST_MATRIX.md +802 -0
- package/docs/GITHUB_AUTHORIZATION.md +38 -0
- package/docs/HEAD_TO_HEAD_EVALUATION.md +79 -0
- package/docs/PROVENANCE_AND_COMPARISON.md +72 -0
- package/docs/PROVIDER_CONFIGURATION.md +45 -0
- package/docs/RELEASE_REVIEW.md +117 -0
- package/docs/SAFE_UPDATE_DEMO.md +25 -0
- package/docs/SCHEMA_DECISIONS.md +25 -0
- package/docs/TEAM_POLICY.md +18 -0
- package/docs/TESTING.md +190 -0
- package/package.json +82 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { planSkillInstall } from "./skills.js";
|
|
3
|
+
import { formatTerminalTable } from "./terminal.js";
|
|
4
|
+
/**
|
|
5
|
+
* Rules shared by the CLI, planner, and documentation. A capability only
|
|
6
|
+
* changes state when it is native or has this explicitly tested adaptation.
|
|
7
|
+
*/
|
|
8
|
+
export const COMPATIBILITY_RULES = {
|
|
9
|
+
native: "Loadout writes the component in a documented, agent-owned layout without translating its format.",
|
|
10
|
+
adapted: "Loadout uses a reviewed agent-specific layout or config writer and reports the adaptation; plugin runtime behavior is never inferred.",
|
|
11
|
+
unsupported: "Loadout does not guess a path, translate the component, or write files for this agent/component combination.",
|
|
12
|
+
};
|
|
13
|
+
const unsupported = {
|
|
14
|
+
skill: "unsupported",
|
|
15
|
+
rule: "unsupported",
|
|
16
|
+
command: "unsupported",
|
|
17
|
+
agent: "unsupported",
|
|
18
|
+
mcp: "unsupported",
|
|
19
|
+
plugin: "unsupported",
|
|
20
|
+
root: "native",
|
|
21
|
+
};
|
|
22
|
+
export const ADAPTER_CAPABILITIES = [
|
|
23
|
+
{
|
|
24
|
+
agent: "claude-code",
|
|
25
|
+
displayName: "Claude Code",
|
|
26
|
+
components: {
|
|
27
|
+
...unsupported,
|
|
28
|
+
skill: "native",
|
|
29
|
+
command: "native",
|
|
30
|
+
agent: "native",
|
|
31
|
+
plugin: "adapted",
|
|
32
|
+
mcp: "adapted",
|
|
33
|
+
},
|
|
34
|
+
notes: [
|
|
35
|
+
"Plugin contents are normalized; plugin-only runtime behavior is not converted.",
|
|
36
|
+
"MCP requires an explicit JSON config path.",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
agent: "codex",
|
|
41
|
+
displayName: "Codex",
|
|
42
|
+
components: {
|
|
43
|
+
...unsupported,
|
|
44
|
+
skill: "native",
|
|
45
|
+
command: "adapted",
|
|
46
|
+
agent: "native",
|
|
47
|
+
plugin: "adapted",
|
|
48
|
+
mcp: "adapted",
|
|
49
|
+
},
|
|
50
|
+
notes: [
|
|
51
|
+
"Commands use the Codex prompts layout.",
|
|
52
|
+
"Codex MCP support only appends new TOML tables; existing tables are never rewritten.",
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
agent: "cursor",
|
|
57
|
+
displayName: "Cursor",
|
|
58
|
+
components: {
|
|
59
|
+
...unsupported,
|
|
60
|
+
skill: "native",
|
|
61
|
+
rule: "native",
|
|
62
|
+
command: "native",
|
|
63
|
+
agent: "native",
|
|
64
|
+
plugin: "adapted",
|
|
65
|
+
mcp: "adapted",
|
|
66
|
+
},
|
|
67
|
+
notes: ["MCP requires an explicit JSON config path."],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
agent: "gemini-cli",
|
|
71
|
+
displayName: "Gemini CLI",
|
|
72
|
+
components: {
|
|
73
|
+
...unsupported,
|
|
74
|
+
skill: "native",
|
|
75
|
+
command: "native",
|
|
76
|
+
plugin: "adapted",
|
|
77
|
+
},
|
|
78
|
+
notes: ["Only tested filesystem-native components are enabled."],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
agent: "opencode",
|
|
82
|
+
displayName: "OpenCode",
|
|
83
|
+
components: {
|
|
84
|
+
...unsupported,
|
|
85
|
+
skill: "native",
|
|
86
|
+
command: "native",
|
|
87
|
+
agent: "native",
|
|
88
|
+
plugin: "adapted",
|
|
89
|
+
},
|
|
90
|
+
notes: ["Plugin contents are normalized into supported components."],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
agent: "hermes",
|
|
94
|
+
displayName: "Hermes",
|
|
95
|
+
components: { ...unsupported, skill: "native", plugin: "adapted" },
|
|
96
|
+
notes: ["Only skill installation is currently claimed."],
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
agent: "windsurf",
|
|
100
|
+
displayName: "Windsurf",
|
|
101
|
+
components: { ...unsupported, skill: "native" },
|
|
102
|
+
notes: ["Only the documented global Agent Skills directory is managed."],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
agent: "cline",
|
|
106
|
+
displayName: "Cline",
|
|
107
|
+
components: { ...unsupported, skill: "native" },
|
|
108
|
+
notes: ["Only the documented global Agent Skills directory is managed."],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
agent: "github-copilot",
|
|
112
|
+
displayName: "GitHub Copilot",
|
|
113
|
+
components: { ...unsupported, skill: "native" },
|
|
114
|
+
notes: ["Only the documented personal Agent Skills directory is managed."],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
agent: "roo-code",
|
|
118
|
+
displayName: "Roo Code",
|
|
119
|
+
components: { ...unsupported, skill: "native" },
|
|
120
|
+
notes: ["Only the documented global generic skills directory is managed."],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
agent: "kiro-cli",
|
|
124
|
+
displayName: "Kiro CLI",
|
|
125
|
+
components: { ...unsupported, skill: "native" },
|
|
126
|
+
notes: ["Only the documented global Agent Skills directory is managed."],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
agent: "junie",
|
|
130
|
+
displayName: "Junie",
|
|
131
|
+
components: { ...unsupported, skill: "native" },
|
|
132
|
+
notes: [
|
|
133
|
+
"Only the documented user-level Agent Skills directory is managed.",
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
export function adapterCapabilities(agent) {
|
|
138
|
+
const found = ADAPTER_CAPABILITIES.find((entry) => entry.agent === agent);
|
|
139
|
+
if (!found)
|
|
140
|
+
throw new Error(`No adapter capability declaration for '${agent}'`);
|
|
141
|
+
return found;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Produce a read-only native-skill plan for one agent. This is deliberately
|
|
145
|
+
* narrower than package normalization: adapters may only target a directory
|
|
146
|
+
* declared by the capability matrix, and planning never creates that
|
|
147
|
+
* directory or writes agent configuration.
|
|
148
|
+
*/
|
|
149
|
+
export async function planAdapterSkillInstall(source, packageId, agent, options = {}) {
|
|
150
|
+
const capability = adapterCapabilities(agent.id).components.skill;
|
|
151
|
+
if (capability !== "native") {
|
|
152
|
+
throw new Error(`${agent.displayName} does not declare native skill installation support.`);
|
|
153
|
+
}
|
|
154
|
+
const plan = await planSkillInstall(source, [agent.skillsDirectory], packageId, options);
|
|
155
|
+
return {
|
|
156
|
+
...plan,
|
|
157
|
+
files: plan.files.map((file) => ({
|
|
158
|
+
...file,
|
|
159
|
+
targetAgent: agent.id,
|
|
160
|
+
componentType: "skill",
|
|
161
|
+
compatibility: capability,
|
|
162
|
+
})),
|
|
163
|
+
targetAgents: [agent.id],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Return only the filesystem directory that this adapter actually manages for
|
|
168
|
+
* a component. Undefined is intentional for unsupported components and for
|
|
169
|
+
* explicit/config-scoped features such as MCP; callers must not guess paths.
|
|
170
|
+
*/
|
|
171
|
+
export function agentComponentDirectory(agent, type) {
|
|
172
|
+
const compatibility = adapterCapabilities(agent.id).components[type];
|
|
173
|
+
if (compatibility === "unsupported")
|
|
174
|
+
return undefined;
|
|
175
|
+
if (type === "skill")
|
|
176
|
+
return agent.skillsDirectory;
|
|
177
|
+
const skillBase = dirname(agent.skillsDirectory);
|
|
178
|
+
if (agent.id === "claude-code" &&
|
|
179
|
+
(type === "rule" || type === "command" || type === "agent"))
|
|
180
|
+
return join(skillBase, `${type}s`);
|
|
181
|
+
if (agent.id === "codex") {
|
|
182
|
+
const home = dirname(dirname(agent.skillsDirectory));
|
|
183
|
+
if (type === "command")
|
|
184
|
+
return join(home, ".codex", "prompts");
|
|
185
|
+
if (type === "agent")
|
|
186
|
+
return join(home, ".codex", "agents");
|
|
187
|
+
}
|
|
188
|
+
if (agent.id === "cursor" &&
|
|
189
|
+
(type === "rule" || type === "command" || type === "agent"))
|
|
190
|
+
return join(skillBase, `${type}s`);
|
|
191
|
+
if (agent.id === "gemini-cli" && type === "command")
|
|
192
|
+
return join(skillBase, "commands");
|
|
193
|
+
if (agent.id === "opencode" && (type === "command" || type === "agent"))
|
|
194
|
+
return join(skillBase, `${type}s`);
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
export function formatCapabilityMatrix(width) {
|
|
198
|
+
const types = [
|
|
199
|
+
"skill",
|
|
200
|
+
"rule",
|
|
201
|
+
"command",
|
|
202
|
+
"agent",
|
|
203
|
+
"mcp",
|
|
204
|
+
"plugin",
|
|
205
|
+
"root",
|
|
206
|
+
];
|
|
207
|
+
return formatTerminalTable(["Agent", ...types], ADAPTER_CAPABILITIES.map((entry) => [
|
|
208
|
+
entry.displayName,
|
|
209
|
+
...types.map((type) => entry.components[type]),
|
|
210
|
+
]), width);
|
|
211
|
+
}
|
|
212
|
+
export function compatibilityRule(compatibility) {
|
|
213
|
+
return COMPATIBILITY_RULES[compatibility];
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Expose unsupported combinations as an engineering backlog. This report does
|
|
217
|
+
* not infer a filesystem path or imply that popularity is adapter evidence.
|
|
218
|
+
*/
|
|
219
|
+
export function buildAdapterCapabilityGaps() {
|
|
220
|
+
const demand = {
|
|
221
|
+
skill: 6,
|
|
222
|
+
mcp: 5,
|
|
223
|
+
command: 4,
|
|
224
|
+
agent: 3,
|
|
225
|
+
rule: 2,
|
|
226
|
+
plugin: 1,
|
|
227
|
+
};
|
|
228
|
+
return ADAPTER_CAPABILITIES.flatMap((entry) => Object.entries(entry.components)
|
|
229
|
+
.filter((item) => item[0] !== "root" && item[1] === "unsupported")
|
|
230
|
+
.map(([component]) => ({
|
|
231
|
+
agent: entry.agent,
|
|
232
|
+
displayName: entry.displayName,
|
|
233
|
+
component,
|
|
234
|
+
priority: demand[component],
|
|
235
|
+
requirement: "Official path/config documentation, a non-mutating planner, preservation fixtures, transaction tests, and a real disposable smoke test are required before support is claimed.",
|
|
236
|
+
}))).sort((left, right) => right.priority - left.priority ||
|
|
237
|
+
left.displayName.localeCompare(right.displayName) ||
|
|
238
|
+
left.component.localeCompare(right.component));
|
|
239
|
+
}
|
|
240
|
+
export function formatAdapterCapabilityGaps() {
|
|
241
|
+
const gaps = buildAdapterCapabilityGaps();
|
|
242
|
+
const counts = new Map();
|
|
243
|
+
for (const gap of gaps)
|
|
244
|
+
counts.set(gap.component, (counts.get(gap.component) ?? 0) + 1);
|
|
245
|
+
return [
|
|
246
|
+
`Unsupported adapter combinations: ${gaps.length}`,
|
|
247
|
+
`By component: ${[...counts.entries()]
|
|
248
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
249
|
+
.map(([component, count]) => `${component} ${count}`)
|
|
250
|
+
.join(", ")}`,
|
|
251
|
+
"",
|
|
252
|
+
...gaps.map((gap) => `${gap.displayName} / ${gap.component} — priority ${gap.priority} — documentation and conformance evidence required`),
|
|
253
|
+
].join("\n");
|
|
254
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import { enrichInventoryWithProvenance } from "./provenance.js";
|
|
5
|
+
import { scanInstalledSkills } from "./skill-inventory.js";
|
|
6
|
+
import { installStatePath, recordInstall } from "./state.js";
|
|
7
|
+
import { runMutationTransaction } from "./transaction.js";
|
|
8
|
+
function slug(value) {
|
|
9
|
+
return (value
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
12
|
+
.replace(/^-|-$/g, "")
|
|
13
|
+
.slice(0, 60) || "skill");
|
|
14
|
+
}
|
|
15
|
+
export async function planSkillAdoption(skill, agent, index) {
|
|
16
|
+
const inventory = enrichInventoryWithProvenance(await scanInstalledSkills([agent]), index, index ? "cache" : "none");
|
|
17
|
+
const query = skill.trim().toLowerCase();
|
|
18
|
+
const matches = inventory.skills.filter((entry) => entry.name.toLowerCase() === query ||
|
|
19
|
+
basename(entry.path).toLowerCase() === query ||
|
|
20
|
+
entry.path === skill);
|
|
21
|
+
if (!matches.length)
|
|
22
|
+
throw new Error(`No installed skill named '${skill}' was found for ${agent.displayName}`);
|
|
23
|
+
if (matches.length > 1)
|
|
24
|
+
throw new Error(`Skill '${skill}' is ambiguous for ${agent.displayName}: ${matches.map((entry) => entry.path).join(", ")}`);
|
|
25
|
+
const match = matches[0];
|
|
26
|
+
if (match.managed)
|
|
27
|
+
throw new Error(`Skill '${skill}' is already managed as ${match.packageId}`);
|
|
28
|
+
const exact = match.provenance.kind === "catalog-exact"
|
|
29
|
+
? match.provenance.candidates[0]
|
|
30
|
+
: undefined;
|
|
31
|
+
const packageId = `adopted-${agent.id}-${slug(match.name)}`;
|
|
32
|
+
return {
|
|
33
|
+
packageId,
|
|
34
|
+
agent,
|
|
35
|
+
name: match.name,
|
|
36
|
+
path: match.path,
|
|
37
|
+
fingerprint: match.fingerprint,
|
|
38
|
+
provenance: match.provenance,
|
|
39
|
+
reviewed: Boolean(exact),
|
|
40
|
+
...(exact
|
|
41
|
+
? { repository: exact.repository, resolvedCommit: exact.commit }
|
|
42
|
+
: {}),
|
|
43
|
+
installPlan: {
|
|
44
|
+
packageId,
|
|
45
|
+
targetAgents: [agent.id],
|
|
46
|
+
warnings: exact
|
|
47
|
+
? []
|
|
48
|
+
: [
|
|
49
|
+
"The installed bytes do not exactly match the reviewed catalog; adoption records ownership but does not mark them reviewed.",
|
|
50
|
+
],
|
|
51
|
+
files: [
|
|
52
|
+
{
|
|
53
|
+
source: match.path,
|
|
54
|
+
target: match.path,
|
|
55
|
+
targetAgent: agent.id,
|
|
56
|
+
componentType: "skill",
|
|
57
|
+
compatibility: "native",
|
|
58
|
+
skillName: match.name,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export async function applySkillAdoption(plan) {
|
|
65
|
+
const applied = await runMutationTransaction(async () => {
|
|
66
|
+
const current = createHash("sha256")
|
|
67
|
+
.update(await readFile(join(plan.path, "SKILL.md")))
|
|
68
|
+
.digest("hex");
|
|
69
|
+
if (current !== plan.fingerprint)
|
|
70
|
+
throw new Error("The skill changed after preview; scan again before adopting it.");
|
|
71
|
+
return { targets: [installStatePath()], value: plan };
|
|
72
|
+
}, async (freshPlan, snapshot) => {
|
|
73
|
+
await recordInstall(freshPlan.installPlan, snapshot.id, {
|
|
74
|
+
...(freshPlan.repository ? { repository: freshPlan.repository } : {}),
|
|
75
|
+
...(freshPlan.resolvedCommit
|
|
76
|
+
? { resolvedCommit: freshPlan.resolvedCommit }
|
|
77
|
+
: {}),
|
|
78
|
+
reviewed: freshPlan.reviewed,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
return applied.snapshotId;
|
|
82
|
+
}
|
|
83
|
+
export function formatAdoptionPlan(plan) {
|
|
84
|
+
return [
|
|
85
|
+
`Adopt: ${plan.name} for ${plan.agent.displayName}`,
|
|
86
|
+
`Path: ${plan.path}`,
|
|
87
|
+
`Managed id: ${plan.packageId}`,
|
|
88
|
+
`Provenance: ${plan.provenance.kind} (${plan.provenance.confidence})`,
|
|
89
|
+
`Review state: ${plan.reviewed ? "reviewed exact catalog match" : "unreviewed"}`,
|
|
90
|
+
...plan.installPlan.warnings.map((warning) => `Warning: ${warning}`),
|
|
91
|
+
].join("\n");
|
|
92
|
+
}
|