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,336 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Runtime schemas for data that crosses a trust boundary: catalog JSON,
|
|
4
|
+
* manifests and lockfiles shared between machines, persisted install state,
|
|
5
|
+
* and install plans passed between Loadout modules. These schemas deliberately
|
|
6
|
+
* do not model secrets; MCP environment values are never persisted here.
|
|
7
|
+
*/
|
|
8
|
+
export const agentIdSchema = z.enum([
|
|
9
|
+
"claude-code",
|
|
10
|
+
"codex",
|
|
11
|
+
"cursor",
|
|
12
|
+
"gemini-cli",
|
|
13
|
+
"opencode",
|
|
14
|
+
"hermes",
|
|
15
|
+
"windsurf",
|
|
16
|
+
"cline",
|
|
17
|
+
"github-copilot",
|
|
18
|
+
"roo-code",
|
|
19
|
+
"kiro-cli",
|
|
20
|
+
"junie",
|
|
21
|
+
]);
|
|
22
|
+
export const packageTierSchema = z.enum([
|
|
23
|
+
"official",
|
|
24
|
+
"stable",
|
|
25
|
+
"trending",
|
|
26
|
+
"community",
|
|
27
|
+
]);
|
|
28
|
+
export const operatingSystemSchema = z.enum(["windows", "macos", "linux"]);
|
|
29
|
+
export const componentTypeSchema = z.enum([
|
|
30
|
+
"skill",
|
|
31
|
+
"rule",
|
|
32
|
+
"command",
|
|
33
|
+
"agent",
|
|
34
|
+
"mcp",
|
|
35
|
+
"plugin",
|
|
36
|
+
"root",
|
|
37
|
+
]);
|
|
38
|
+
export const componentCompatibilitySchema = z.enum([
|
|
39
|
+
"native",
|
|
40
|
+
"adapted",
|
|
41
|
+
"unsupported",
|
|
42
|
+
]);
|
|
43
|
+
export const safetyRiskLevelSchema = z.enum(["safe", "review", "blocked"]);
|
|
44
|
+
const text = z.string().trim().min(1, "must not be empty");
|
|
45
|
+
const optionalText = text.optional();
|
|
46
|
+
const sha256 = z.string().regex(/^[a-f0-9]{64}$/i, "must be a SHA-256 hash");
|
|
47
|
+
const gitSha = z.string().regex(/^[a-f0-9]{40}$/i, "must be a full Git SHA");
|
|
48
|
+
const repository = z
|
|
49
|
+
.string()
|
|
50
|
+
.regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/, "must be owner/repository");
|
|
51
|
+
const safeRepositoryPath = z
|
|
52
|
+
.string()
|
|
53
|
+
.min(1)
|
|
54
|
+
.refine((value) => !value.startsWith("/") &&
|
|
55
|
+
!value.startsWith("\\") &&
|
|
56
|
+
!value.split(/[\\/]/).includes(".."), "must be a safe repository-relative path");
|
|
57
|
+
const secretLikeValue = /^(?:(?:sk|rk|pk)[_-]|ghp_|github_pat_)[A-Za-z0-9_-]{12,}$/i;
|
|
58
|
+
const safeModelText = z
|
|
59
|
+
.string()
|
|
60
|
+
.trim()
|
|
61
|
+
.min(1, "must not be empty")
|
|
62
|
+
.max(200, "must be at most 200 characters")
|
|
63
|
+
.refine((value) => !secretLikeValue.test(value), "must not contain a credential value");
|
|
64
|
+
const providerIdSchema = safeModelText.regex(/^[a-z0-9]+(?:[a-z0-9-]*[a-z0-9])?$/, "must be a lowercase provider identifier");
|
|
65
|
+
const environmentVariableNameSchema = z
|
|
66
|
+
.string()
|
|
67
|
+
.regex(/^[A-Z_][A-Z0-9_]*$/, "must be an environment variable name, not its value")
|
|
68
|
+
.max(128);
|
|
69
|
+
const httpsEndpointSchema = z.url("must be a URL").refine((value) => {
|
|
70
|
+
const endpoint = new URL(value);
|
|
71
|
+
return (endpoint.protocol === "https:" &&
|
|
72
|
+
!endpoint.username &&
|
|
73
|
+
!endpoint.password &&
|
|
74
|
+
!endpoint.search &&
|
|
75
|
+
!endpoint.hash);
|
|
76
|
+
}, "must be an HTTPS URL without credentials, query parameters, or fragments");
|
|
77
|
+
export const credentialReferenceSchema = z.discriminatedUnion("kind", [
|
|
78
|
+
z
|
|
79
|
+
.object({
|
|
80
|
+
kind: z.literal("environment"),
|
|
81
|
+
name: environmentVariableNameSchema,
|
|
82
|
+
})
|
|
83
|
+
.strict(),
|
|
84
|
+
z
|
|
85
|
+
.object({
|
|
86
|
+
kind: z.literal("os-keychain"),
|
|
87
|
+
service: safeModelText,
|
|
88
|
+
account: safeModelText.optional(),
|
|
89
|
+
})
|
|
90
|
+
.strict(),
|
|
91
|
+
]);
|
|
92
|
+
export const providerModelSelectionSchema = z
|
|
93
|
+
.object({
|
|
94
|
+
id: providerIdSchema,
|
|
95
|
+
provider: providerIdSchema,
|
|
96
|
+
model: safeModelText,
|
|
97
|
+
endpoint: httpsEndpointSchema,
|
|
98
|
+
credential: credentialReferenceSchema.optional(),
|
|
99
|
+
targetAgents: z.array(agentIdSchema).min(1).optional(),
|
|
100
|
+
})
|
|
101
|
+
.strict();
|
|
102
|
+
/**
|
|
103
|
+
* This deliberately has no apiKey/token/header fields. It is safe to put in a
|
|
104
|
+
* shared Loadout because it contains only metadata and credential references.
|
|
105
|
+
*/
|
|
106
|
+
export const providerModelConfigurationSchema = z
|
|
107
|
+
.object({
|
|
108
|
+
schemaVersion: z.literal(1),
|
|
109
|
+
selections: z.array(providerModelSelectionSchema).min(1),
|
|
110
|
+
})
|
|
111
|
+
.strict()
|
|
112
|
+
.superRefine((value, context) => {
|
|
113
|
+
const seen = new Set();
|
|
114
|
+
for (const [index, selection] of value.selections.entries()) {
|
|
115
|
+
if (seen.has(selection.id)) {
|
|
116
|
+
context.addIssue({
|
|
117
|
+
code: "custom",
|
|
118
|
+
path: ["selections", index, "id"],
|
|
119
|
+
message: "must be unique",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
seen.add(selection.id);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
export const catalogSourceEvidenceSchema = z
|
|
126
|
+
.object({
|
|
127
|
+
type: z.literal("github"),
|
|
128
|
+
url: z.url("must be a URL"),
|
|
129
|
+
defaultBranch: z
|
|
130
|
+
.string()
|
|
131
|
+
.regex(/^[A-Za-z0-9._/-]+$/, "contains unsupported characters"),
|
|
132
|
+
commit: gitSha,
|
|
133
|
+
evidencePaths: z.array(safeRepositoryPath).min(1),
|
|
134
|
+
verifiedAt: z.iso.datetime("must be an ISO timestamp"),
|
|
135
|
+
})
|
|
136
|
+
.passthrough();
|
|
137
|
+
export const catalogPackageSchema = z
|
|
138
|
+
.object({
|
|
139
|
+
id: z
|
|
140
|
+
.string()
|
|
141
|
+
.regex(/^[a-z0-9]+(?:[a-z0-9-]*[a-z0-9])?$/, "must be lowercase kebab-case"),
|
|
142
|
+
displayName: text,
|
|
143
|
+
repository,
|
|
144
|
+
description: text,
|
|
145
|
+
category: text,
|
|
146
|
+
tier: packageTierSchema,
|
|
147
|
+
license: optionalText,
|
|
148
|
+
components: z.array(componentTypeSchema).min(1).optional(),
|
|
149
|
+
operatingSystems: z.array(operatingSystemSchema).min(1).optional(),
|
|
150
|
+
source: catalogSourceEvidenceSchema.optional(),
|
|
151
|
+
stars: z.number().finite().nonnegative().optional(),
|
|
152
|
+
lastUpdatedAt: z.iso.datetime("must be an ISO timestamp").optional(),
|
|
153
|
+
pushedAt: z.iso.datetime("must be an ISO timestamp").optional(),
|
|
154
|
+
topics: z.array(text).optional(),
|
|
155
|
+
openIssues: z.number().int().nonnegative().optional(),
|
|
156
|
+
archived: z.boolean().optional(),
|
|
157
|
+
})
|
|
158
|
+
.passthrough();
|
|
159
|
+
export const catalogSchema = z.array(catalogPackageSchema);
|
|
160
|
+
export const plannedFileSchema = z
|
|
161
|
+
.object({
|
|
162
|
+
source: text,
|
|
163
|
+
target: text,
|
|
164
|
+
targetAgent: agentIdSchema.optional(),
|
|
165
|
+
componentType: componentTypeSchema.optional(),
|
|
166
|
+
compatibility: componentCompatibilitySchema.optional(),
|
|
167
|
+
skillName: optionalText,
|
|
168
|
+
})
|
|
169
|
+
.passthrough();
|
|
170
|
+
export const conflictDiagnosticSchema = z
|
|
171
|
+
.object({
|
|
172
|
+
severity: z.enum(["blocking", "warning"]),
|
|
173
|
+
code: z.enum(["target-collision", "duplicate-skill-name"]),
|
|
174
|
+
message: text,
|
|
175
|
+
packageIds: z.array(text).min(1),
|
|
176
|
+
targets: z.array(text).min(1),
|
|
177
|
+
})
|
|
178
|
+
.passthrough();
|
|
179
|
+
export const installPlanSchema = z
|
|
180
|
+
.object({
|
|
181
|
+
packageId: text,
|
|
182
|
+
files: z.array(plannedFileSchema),
|
|
183
|
+
targetAgents: z.array(agentIdSchema),
|
|
184
|
+
warnings: z.array(z.string()),
|
|
185
|
+
conflicts: z.array(conflictDiagnosticSchema).optional(),
|
|
186
|
+
})
|
|
187
|
+
.passthrough();
|
|
188
|
+
export const packageSourceSchema = z.discriminatedUnion("type", [
|
|
189
|
+
z.object({ type: z.literal("catalog"), id: text }),
|
|
190
|
+
z.object({
|
|
191
|
+
type: z.literal("github"),
|
|
192
|
+
repository,
|
|
193
|
+
ref: optionalText,
|
|
194
|
+
path: optionalText,
|
|
195
|
+
}),
|
|
196
|
+
z.object({
|
|
197
|
+
type: z.literal("git"),
|
|
198
|
+
url: text,
|
|
199
|
+
ref: optionalText,
|
|
200
|
+
path: optionalText,
|
|
201
|
+
}),
|
|
202
|
+
z.object({ type: z.literal("registry"), name: text, version: text }),
|
|
203
|
+
z.object({
|
|
204
|
+
type: z.literal("remote-registry"),
|
|
205
|
+
registry: text,
|
|
206
|
+
name: text,
|
|
207
|
+
version: text,
|
|
208
|
+
}),
|
|
209
|
+
z.object({ type: z.literal("local"), path: text }),
|
|
210
|
+
]);
|
|
211
|
+
const manifestPackageSchema = z
|
|
212
|
+
.object({
|
|
213
|
+
id: text,
|
|
214
|
+
source: packageSourceSchema,
|
|
215
|
+
agents: z.array(agentIdSchema).optional(),
|
|
216
|
+
dependsOn: z.array(text).optional(),
|
|
217
|
+
includeDevDependencies: z.boolean().optional(),
|
|
218
|
+
mcp: z
|
|
219
|
+
.object({ config: text, servers: z.array(text).optional() })
|
|
220
|
+
.optional(),
|
|
221
|
+
rootFiles: z.array(z.object({ source: text, target: text })).optional(),
|
|
222
|
+
enabled: z.boolean().optional(),
|
|
223
|
+
})
|
|
224
|
+
.passthrough();
|
|
225
|
+
export const loadoutManifestSchema = z
|
|
226
|
+
.object({
|
|
227
|
+
schemaVersion: z.literal(1),
|
|
228
|
+
name: text,
|
|
229
|
+
scope: z.enum(["project", "global"]),
|
|
230
|
+
agents: z.array(agentIdSchema),
|
|
231
|
+
profile: optionalText,
|
|
232
|
+
packages: z.array(manifestPackageSchema),
|
|
233
|
+
policy: z
|
|
234
|
+
.object({
|
|
235
|
+
allowRisk: z.array(safetyRiskLevelSchema).optional(),
|
|
236
|
+
blockedDomains: z.array(text).optional(),
|
|
237
|
+
blockedCommands: z.array(text).optional(),
|
|
238
|
+
allowPackages: z.array(text).optional(),
|
|
239
|
+
allowRepositories: z.array(text).optional(),
|
|
240
|
+
deniedPackages: z.array(text).optional(),
|
|
241
|
+
deniedRepositories: z.array(text).optional(),
|
|
242
|
+
requiredApprovals: z.number().int().min(0).optional(),
|
|
243
|
+
})
|
|
244
|
+
// Preserve unknown policy keys so portable-export secret scanning can
|
|
245
|
+
// reject them rather than silently dropping them during validation.
|
|
246
|
+
.passthrough()
|
|
247
|
+
.optional(),
|
|
248
|
+
})
|
|
249
|
+
.passthrough();
|
|
250
|
+
const fileHashSchema = z.object({ path: text, sha256 });
|
|
251
|
+
export const installRecordSchema = z
|
|
252
|
+
.object({
|
|
253
|
+
packageId: text,
|
|
254
|
+
repository: optionalText,
|
|
255
|
+
resolvedCommit: optionalText,
|
|
256
|
+
targetAgents: z.array(agentIdSchema),
|
|
257
|
+
files: z.array(fileHashSchema),
|
|
258
|
+
snapshotId: text,
|
|
259
|
+
installedAt: text,
|
|
260
|
+
})
|
|
261
|
+
.passthrough();
|
|
262
|
+
export const mcpInstallRecordSchema = z
|
|
263
|
+
.object({
|
|
264
|
+
packageId: text,
|
|
265
|
+
configPath: text,
|
|
266
|
+
serverName: text,
|
|
267
|
+
fingerprint: sha256,
|
|
268
|
+
snapshotId: text,
|
|
269
|
+
installedAt: text,
|
|
270
|
+
})
|
|
271
|
+
.passthrough();
|
|
272
|
+
export const managedActivationRecordSchema = z
|
|
273
|
+
.object({
|
|
274
|
+
packageId: text,
|
|
275
|
+
unitId: optionalText,
|
|
276
|
+
agent: agentIdSchema,
|
|
277
|
+
cacheState: z.enum(["missing", "downloaded"]),
|
|
278
|
+
reviewState: z.enum(["unreviewed", "reviewed", "quarantined"]),
|
|
279
|
+
installationState: z.enum(["installed", "removed"]),
|
|
280
|
+
activationState: z.enum(["active", "disabled"]),
|
|
281
|
+
libraryPath: text,
|
|
282
|
+
targets: z.array(z
|
|
283
|
+
.object({
|
|
284
|
+
activePath: text,
|
|
285
|
+
libraryRelativePath: text.refine((value) => value !== "." && value !== ".." && !/[\\/]/.test(value), "must be a single safe path segment"),
|
|
286
|
+
})
|
|
287
|
+
.passthrough()),
|
|
288
|
+
libraryFiles: z.array(fileHashSchema),
|
|
289
|
+
updatedAt: text,
|
|
290
|
+
snapshotId: optionalText,
|
|
291
|
+
})
|
|
292
|
+
.passthrough();
|
|
293
|
+
export const installStateSchema = z
|
|
294
|
+
.object({
|
|
295
|
+
version: z.literal(1),
|
|
296
|
+
installs: z.array(installRecordSchema),
|
|
297
|
+
mcpInstalls: z.array(mcpInstallRecordSchema).default([]),
|
|
298
|
+
activations: z.array(managedActivationRecordSchema).default([]),
|
|
299
|
+
})
|
|
300
|
+
.passthrough();
|
|
301
|
+
const lockedPackageSchema = z
|
|
302
|
+
.object({
|
|
303
|
+
id: text,
|
|
304
|
+
source: packageSourceSchema,
|
|
305
|
+
repository: optionalText,
|
|
306
|
+
resolvedCommit: optionalText,
|
|
307
|
+
targetAgents: z.array(agentIdSchema),
|
|
308
|
+
files: z.array(fileHashSchema),
|
|
309
|
+
installedAt: text,
|
|
310
|
+
dependencies: z.array(text).optional(),
|
|
311
|
+
})
|
|
312
|
+
.passthrough();
|
|
313
|
+
export const loadoutLockfileSchema = z
|
|
314
|
+
.object({
|
|
315
|
+
schemaVersion: z.literal(1),
|
|
316
|
+
manifestName: text,
|
|
317
|
+
// Older lockfiles predate this field. Read them safely as an explicit
|
|
318
|
+
// legacy value; every new lockfile writer still emits an ISO timestamp.
|
|
319
|
+
generatedAt: text.optional().default("unknown"),
|
|
320
|
+
packages: z.array(lockedPackageSchema),
|
|
321
|
+
mcpServers: z
|
|
322
|
+
.array(z.object({
|
|
323
|
+
packageId: text,
|
|
324
|
+
configPath: text,
|
|
325
|
+
serverName: text,
|
|
326
|
+
fingerprint: sha256,
|
|
327
|
+
}))
|
|
328
|
+
.optional(),
|
|
329
|
+
})
|
|
330
|
+
.passthrough();
|
|
331
|
+
/** Compact, path-aware errors suitable for CLI and persisted-data diagnostics. */
|
|
332
|
+
export function formatSchemaError(error) {
|
|
333
|
+
return error.issues
|
|
334
|
+
.map((issue) => `${issue.path.length ? issue.path.join(".") : "value"}: ${issue.message}`)
|
|
335
|
+
.join("; ");
|
|
336
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Reviewed library and active-set contract
|
|
2
|
+
|
|
3
|
+
Loadout separates four facts that package managers often collapse into one word:
|
|
4
|
+
|
|
5
|
+
- **cache**: `missing` or `downloaded` says whether Loadout has a private byte-for-byte
|
|
6
|
+
library copy;
|
|
7
|
+
- **review**: `unreviewed`, `reviewed`, or `quarantined` records evidence policy and
|
|
8
|
+
never follows popularity alone;
|
|
9
|
+
- **installation**: `installed` or `removed` records whether Loadout still manages the
|
|
10
|
+
package; and
|
|
11
|
+
- **activation**: `active` or `disabled` is per skill unit and per agent, because one
|
|
12
|
+
skill from a collection can be visible to Claude Code while another stays disabled
|
|
13
|
+
for Codex.
|
|
14
|
+
|
|
15
|
+
Maximum Library starts reviewed skill units as `downloaded + reviewed + installed +
|
|
16
|
+
disabled`. It can therefore hold hundreds of candidates without exposing hundreds of
|
|
17
|
+
instructions to an agent. Power is an intentionally broader active profile; Stable is
|
|
18
|
+
the smallest active foundation.
|
|
19
|
+
|
|
20
|
+
An initial install is active and may have `cache:missing`. On the first disable,
|
|
21
|
+
Loadout verifies that every managed file is unchanged and that no untracked file would
|
|
22
|
+
be removed, copies the complete skill directories into its private library, verifies
|
|
23
|
+
their hashes, and only then removes the agent-visible copies. This is the explicit
|
|
24
|
+
migration boundary for older installations.
|
|
25
|
+
|
|
26
|
+
`enable` verifies the private library hashes and refuses to overwrite any occupied
|
|
27
|
+
agent target. It restores the exact cached bytes but keeps the library copy. Both
|
|
28
|
+
commands are dry-run by default, can change several packages and agents under one
|
|
29
|
+
snapshot, write a durable transaction journal before mutation, and can be reversed by
|
|
30
|
+
`loadout rollback --snapshot <id>`.
|
|
31
|
+
|
|
32
|
+
Only Loadout-managed skill directories participate. Existing unmanaged content is
|
|
33
|
+
never silently adopted, moved, or deleted. MCP entries and other executable/configured
|
|
34
|
+
components are deliberately outside active-set toggling; they retain their existing
|
|
35
|
+
explicit configuration and removal workflows.
|
|
36
|
+
|
|
37
|
+
A disabled package must be enabled before its tracked source can be updated. This
|
|
38
|
+
prevents the update workflow from silently recreating agent-visible files and makes the
|
|
39
|
+
activation transition explicit.
|
|
40
|
+
|
|
41
|
+
## Commands
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
loadout library
|
|
45
|
+
loadout disable <package-or-package/skill> [more-selectors] [--agents codex,claude-code]
|
|
46
|
+
loadout disable <package-or-package/skill> --yes
|
|
47
|
+
loadout enable <package-or-package/skill> --yes
|
|
48
|
+
loadout activate --project . --limit 40
|
|
49
|
+
loadout optimize --project .
|
|
50
|
+
loadout rollback --snapshot <id>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Machine-readable planning and results are available with `--json`.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Active-set selection policy
|
|
2
|
+
|
|
3
|
+
Loadout treats the reviewed library as inventory and the active set as a constrained,
|
|
4
|
+
per-agent decision. It never equates repository popularity with universal quality.
|
|
5
|
+
The policy is deterministic, local by default, explainable in the preview, and ordered
|
|
6
|
+
by these constraints:
|
|
7
|
+
|
|
8
|
+
1. Block quarantined, removed, incompatible, missing-cache, and unreviewed candidates.
|
|
9
|
+
2. Honor an explicit full `package/skill` pin before defaults. A short skill-name pin
|
|
10
|
+
may match a reviewed equivalent, but cannot bypass a safety blocker.
|
|
11
|
+
3. Preserve verified hard conflicts and choose only one default from equivalent skill
|
|
12
|
+
names. Other reviewed sources remain disabled and are shown as alternatives.
|
|
13
|
+
4. Score project language/framework signals and broad task families such as planning,
|
|
14
|
+
debugging, testing, review, security, documentation, architecture, and delivery.
|
|
15
|
+
A generic word match is supporting evidence, not enough to activate a specialized
|
|
16
|
+
domain tool by itself.
|
|
17
|
+
5. Add cross-project foundations and stop at the user-specified per-agent capacity.
|
|
18
|
+
Capacity is a ceiling, never a quota; unused slots are safer than irrelevant skills.
|
|
19
|
+
6. Use category-specific evaluation confidence when evidence exists; no benchmark is
|
|
20
|
+
treated as universal across tasks, agents, models, or versions.
|
|
21
|
+
7. Apply explicit local human outcomes only within the same task/agent scope. Pins,
|
|
22
|
+
rollback, rejection, and later re-activation outweigh aggregate popularity. Until
|
|
23
|
+
the privacy-safe outcome store is complete, this term is neutral rather than
|
|
24
|
+
guessed.
|
|
25
|
+
8. Use maintenance and bounded adoption evidence only as a final tie-breaker. Stars
|
|
26
|
+
cannot override safety, compatibility, conflicts, pins, evaluations, or local
|
|
27
|
+
outcomes.
|
|
28
|
+
|
|
29
|
+
The current project selector implements steps 1–5 and exposes its scores, reasons,
|
|
30
|
+
equivalent-source decisions, exact activation delta, hash verification, and rollback
|
|
31
|
+
snapshot. Steps 6–7 are deliberately connected to P12-15 through P12-20 rather than
|
|
32
|
+
filled with fake benchmark or feedback data.
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
loadout activate --project . --limit 40
|
|
38
|
+
loadout activate --project . --pin openai-skills/playwright
|
|
39
|
+
loadout optimize --project .
|
|
40
|
+
loadout optimize --project . --yes
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
All commands are dry-run unless `--yes` is present. Project scanning reads only
|
|
44
|
+
well-known root metadata files and does not require GitHub, upload source code, or
|
|
45
|
+
execute a skill.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Candidate intelligence and catalog trust
|
|
2
|
+
|
|
3
|
+
Loadout deliberately separates discovery, inspection, catalog admission, and
|
|
4
|
+
installation. A repository can be popular or fast-growing without being useful,
|
|
5
|
+
compatible, licensed for the intended use, safe, or better than a catalog package.
|
|
6
|
+
|
|
7
|
+
## Evidence pipeline
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
daily GitHub observations
|
|
11
|
+
-> candidate list (triage only)
|
|
12
|
+
-> immutable source snapshot
|
|
13
|
+
-> static dossier
|
|
14
|
+
-> human-reviewed proposal
|
|
15
|
+
-> technically screened catalog JSON
|
|
16
|
+
-> Ed25519-signed release
|
|
17
|
+
-> explicit local diff and apply
|
|
18
|
+
-> normal preview/install/rollback workflow
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
No step silently skips the next authority boundary.
|
|
22
|
+
|
|
23
|
+
## Triage today's feed
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
loadout candidate list --limit 20
|
|
27
|
+
loadout candidate list --query "codex skills" --json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The displayed `triagePriority` is deterministic discovery ordering, not a quality or
|
|
31
|
+
safety score. It is capped at 100 points: up to 30 for bounded-query coverage, 35 for
|
|
32
|
+
the disclosed star-growth signal, 25 for log-scaled adoption, and 10 for appearing in
|
|
33
|
+
the latest run. Before a complete observation day exists, growth is labelled
|
|
34
|
+
`lifetime-star-average`; it is never mislabelled as measured velocity.
|
|
35
|
+
|
|
36
|
+
## Inspect real source safely
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
loadout candidate inspect owner/repository --output ./candidate-dossier.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Loadout performs a shallow public Git clone, resolves the result to a full immutable
|
|
43
|
+
commit, and scans files statically. The dossier records:
|
|
44
|
+
|
|
45
|
+
- discovery date, queries, adoption and growth evidence;
|
|
46
|
+
- immutable repository, commit, branch, and evidence paths;
|
|
47
|
+
- installability as portable components, explicit runtime setup, or unsupported
|
|
48
|
+
source shape;
|
|
49
|
+
- observed skills, rules, commands, agents, plugin manifests, and MCP declarations;
|
|
50
|
+
- secret/instruction/static-safety findings and explicit uncertainty;
|
|
51
|
+
- possible catalog overlaps based on disclosed shared terms; and
|
|
52
|
+
- reasons a human must block or review the candidate.
|
|
53
|
+
|
|
54
|
+
Absolute cache paths are removed from the dossier. Copy the path printed by `--write`,
|
|
55
|
+
or use `--output` as above for a shell- and platform-independent path. Loadout does not
|
|
56
|
+
run install scripts, package lifecycle commands, hooks, MCP servers, candidate models,
|
|
57
|
+
or arbitrary repository executables.
|
|
58
|
+
|
|
59
|
+
An `explicit-runtime-setup` or `unsupported-source-shape` result is not a dead end and
|
|
60
|
+
does not authorize execution. A maintainer may create a separately reviewed runtime
|
|
61
|
+
recipe with an immutable executable artifact, bounded commands, isolated state,
|
|
62
|
+
credential stripping, preview, verification, rollback, and removal. Graphify 0.9.17
|
|
63
|
+
is the first such recipe and is invoked with `loadout tool graphify`; it is not part
|
|
64
|
+
of broad `setup`.
|
|
65
|
+
|
|
66
|
+
Candidate fetches use a two-minute timeout and an isolated Git environment: system
|
|
67
|
+
and global config, templates, hooks, credential helpers, inherited `GIT_*` overrides,
|
|
68
|
+
and LFS smudging cannot affect provenance or execute during checkout. Before blob
|
|
69
|
+
materialization, a bounded GitHub tree-API response must be complete and prove every
|
|
70
|
+
blob size, at most 100 MiB total, and at most 20,000 files. The checked-out snapshot
|
|
71
|
+
is measured again before static analysis; rejected temporary clones are removed.
|
|
72
|
+
|
|
73
|
+
## Create a human-gated proposal
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
loadout candidate propose ./candidate-dossier.json \
|
|
77
|
+
--id package-id --category workflow \
|
|
78
|
+
--platforms windows,macos,linux
|
|
79
|
+
|
|
80
|
+
loadout candidate propose ./candidate-dossier.json \
|
|
81
|
+
--id package-id --category workflow \
|
|
82
|
+
--platforms windows,macos,linux \
|
|
83
|
+
--approve --output ./package-id.proposal.json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Platforms are mandatory human claims; Loadout does not infer them from stars or a
|
|
87
|
+
successful clone. Before proposal preview or output, Loadout reopens the exact pinned
|
|
88
|
+
commit and recomputes the complete inspection and static evaluation; edited evidence
|
|
89
|
+
is rejected. A blocked dossier cannot become a proposal. `--approve` only writes an
|
|
90
|
+
isolated proposal record—it never edits the bundled catalog and never installs the
|
|
91
|
+
candidate. A maintainer must still review usefulness, overlap, license, runtime
|
|
92
|
+
powers, platform behavior, category, and catalog policy.
|
|
93
|
+
|
|
94
|
+
## Distribute a trusted catalog release
|
|
95
|
+
|
|
96
|
+
Maintainers sign a technically screened full catalog array with an Ed25519 key kept outside the
|
|
97
|
+
repository:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
loadout keygen --private-key /secure/catalog-private.pem \
|
|
101
|
+
--public-key ./catalog-public.pem
|
|
102
|
+
loadout catalog-sign --catalog ./catalog/packages.json \
|
|
103
|
+
--private-key /secure/catalog-private.pem \
|
|
104
|
+
--output ./catalog.signed.json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Users preview the verified release before trusting it:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
loadout catalog-update --source ./catalog.signed.json \
|
|
111
|
+
--public-key ./catalog-public.pem
|
|
112
|
+
loadout catalog-update --source ./catalog.signed.json \
|
|
113
|
+
--public-key ./catalog-public.pem --yes
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`--source` also accepts HTTPS with a 15-second timeout and streaming 5 MiB limit; other remote
|
|
117
|
+
schemes are rejected. The command verifies the signature, fingerprint, complete
|
|
118
|
+
catalog schema, immutable evidence, and exact add/update/remove diff. Removals require
|
|
119
|
+
the separate `--allow-removals` acknowledgement. Apply rejects replayed/older releases,
|
|
120
|
+
snapshots previous state, writes atomically, journals the transaction, and re-verifies
|
|
121
|
+
the stored signature whenever the effective catalog loads. The first successful apply
|
|
122
|
+
pins the signing key and a non-rollback replay high-water mark; signer changes require
|
|
123
|
+
an explicit trust-reset/key-rotation decision outside the normal update command.
|
|
124
|
+
|
|
125
|
+
The public key is stored beside the signed envelope because it is verification data,
|
|
126
|
+
not a secret. Trust in that key still has to come from a separately authenticated
|
|
127
|
+
project release channel.
|
|
128
|
+
|
|
129
|
+
## Local personalization
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
loadout recommend --project . --agent codex --json
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
When an agent is supplied, baseline project recommendations are adjusted only by the
|
|
136
|
+
local outcome store for the same agent and task family. The evidence contains no
|
|
137
|
+
project paths, prompts, code, or source content. It can reorder or lower confidence in
|
|
138
|
+
catalog packages, but it cannot promote a discovery candidate or create a global
|
|
139
|
+
popularity loop.
|