loadout-ai 0.7.0 → 0.9.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/CHANGELOG.md +148 -1
- package/README.md +160 -315
- package/SECURITY.md +21 -1
- package/catalog/discovered.json +31269 -26003
- package/catalog/packages.json +4 -4
- package/dist/src/cli.js +13 -0
- package/dist/src/commands/agents.js +3 -1
- package/dist/src/commands/catalog-candidate.js +150 -0
- package/dist/src/commands/catalog-workflows.js +147 -0
- package/dist/src/commands/catalog.js +9 -367
- package/dist/src/commands/coordinate.js +586 -0
- package/dist/src/commands/coordination-discussions.js +194 -0
- package/dist/src/commands/coordination-sessions.js +197 -0
- package/dist/src/commands/inventory.js +4 -2
- package/dist/src/core/agents/agent-inspection.js +26 -4
- package/dist/src/core/{routing → agents}/model-config.js +7 -2
- package/dist/src/core/catalog/catalog.js +1 -0
- package/dist/src/core/catalog/registry.js +44 -10
- package/dist/src/core/catalog/safety.js +36 -7
- package/dist/src/core/coordination/adapters/claude-code.js +154 -0
- package/dist/src/core/coordination/adapters/codex.js +137 -0
- package/dist/src/core/coordination/adapters/types.js +64 -0
- package/dist/src/core/coordination/auth.js +59 -0
- package/dist/src/core/coordination/bridge-lease.js +79 -0
- package/dist/src/core/coordination/conflict-preview.js +167 -0
- package/dist/src/core/coordination/contract-diff.js +106 -0
- package/dist/src/core/coordination/coordinator.js +552 -0
- package/dist/src/core/coordination/crash-recovery.js +169 -0
- package/dist/src/core/coordination/daemon.js +667 -0
- package/dist/src/core/coordination/discussion.js +340 -0
- package/dist/src/core/coordination/events.js +161 -0
- package/dist/src/core/coordination/http-api.js +118 -0
- package/dist/src/core/coordination/interrupt-policy.js +89 -0
- package/dist/src/core/coordination/lock.js +110 -0
- package/dist/src/core/coordination/mcp-server.js +424 -0
- package/dist/src/core/coordination/redaction.js +85 -0
- package/dist/src/core/coordination/replay.js +188 -0
- package/dist/src/core/coordination/retention.js +128 -0
- package/dist/src/core/coordination/runtime.js +21 -0
- package/dist/src/core/coordination/session-manager.js +366 -0
- package/dist/src/core/coordination/watcher.js +128 -0
- package/dist/src/core/{routing → delegation}/first-party-skills.js +25 -13
- package/dist/src/core/{routing → delegation}/handoff.js +130 -63
- package/dist/src/core/discovery/candidate-intelligence-evidence.js +108 -0
- package/dist/src/core/discovery/candidate-intelligence-types.js +1 -0
- package/dist/src/core/discovery/candidate-intelligence-validation.js +108 -0
- package/dist/src/core/discovery/candidate-intelligence.js +3 -214
- package/dist/src/core/discovery/community.js +12 -4
- package/dist/src/core/discovery/github-discovery.js +6 -2
- package/dist/src/core/discovery/private-discovery.js +6 -2
- package/dist/src/core/install/reconcile.js +1 -1
- package/dist/src/core/install/source.js +21 -7
- package/dist/src/core/install/uninstall.js +39 -3
- package/dist/src/core/reporting/cli-guide.js +10 -6
- package/dist/src/core/reporting/completion.js +61 -109
- package/dist/src/core/reporting/doctor.js +4 -6
- package/dist/src/core/runtime/bounded-json.js +65 -0
- package/dist/src/core/runtime/github.js +30 -16
- package/dist/src/core/runtime/mcp-recipes.js +1 -1
- package/dist/src/core/workspace/active-policy.js +1 -1
- package/docs/CANDIDATE_INTELLIGENCE.md +9 -2
- package/docs/CATALOG.md +1 -1
- package/docs/CREDENTIAL_AND_UPDATE_POLICY.md +1 -1
- package/docs/DEMO_SCRIPT.md +19 -23
- package/docs/DISCOVERED.md +251 -250
- package/docs/FEATURE_TEST_MATRIX.md +10 -263
- package/docs/GITHUB_AUTHORIZATION.md +5 -0
- package/docs/LIVE_COLLABORATION.md +234 -0
- package/docs/PROVENANCE_AND_COMPARISON.md +1 -1
- package/docs/REFERENCE.md +163 -0
- package/docs/RELEASE_REVIEW.md +1 -2
- package/docs/TESTING.md +17 -1
- package/docs/USER_TEST_GUIDE.md +138 -3
- package/docs/assets/loadout-discover-activate.webp +0 -0
- package/docs/assets/loadout-handoff-coordinate.webp +0 -0
- package/docs/assets/loadout-social-preview.png +0 -0
- package/docs/decisions/001-coordination-jsonl-locking.md +35 -0
- package/docs/decisions/002-local-daemon-authentication.md +32 -0
- package/docs/decisions/003-bounded-agent-discussions.md +91 -0
- package/docs/specs/BOUNDED_AGENT_DISCUSSIONS.md +169 -0
- package/docs/superpowers/plans/2026-09-03-coordination-hardening.md +231 -0
- package/docs/superpowers/plans/2026-09-03-release-readiness.md +232 -0
- package/docs/superpowers/plans/2026-09-04-bounded-agent-discussions.md +121 -0
- package/package.json +18 -7
- package/skills/loadout-handoff/SKILL.md +238 -0
- package/MASTER_PLAN.md +0 -2207
- package/dist/src/core/routing/route.js +0 -539
- package/docs/ACTIVE_SET.md +0 -53
- package/docs/COMPATIBILITY_POLICY.md +0 -22
- package/docs/CONVERSION_AND_SANDBOX.md +0 -27
- package/docs/EVALUATION_PROTOCOL_V1.md +0 -300
- package/docs/HEAD_TO_HEAD_EVALUATION.md +0 -79
- package/docs/PROVIDER_CONFIGURATION.md +0 -45
- package/docs/README_RESEARCH.md +0 -36
- package/docs/REPOSITORY_STABILIZATION.md +0 -190
- package/docs/SAFE_UPDATE_DEMO.md +0 -25
- package/docs/SCHEMA_DECISIONS.md +0 -25
- package/docs/SUBMISSION_COPY.md +0 -90
- package/docs/TEAM_POLICY.md +0 -18
- package/docs/assets/loadout-workflow.png +0 -0
- package/docs/superpowers/plans/2026-07-19-relatable-readme-hero.md +0 -283
- package/docs/superpowers/plans/2026-07-20-loadout-readme-explainer.md +0 -116
- package/docs/superpowers/plans/2026-07-20-project-activation-safety.md +0 -469
- package/docs/superpowers/specs/2026-07-19-relatable-readme-hero-design.md +0 -80
- package/docs/superpowers/specs/2026-07-20-loadout-readme-explainer-design.md +0 -55
- package/docs/superpowers/specs/2026-07-20-project-activation-safety-design.md +0 -228
- package/skills/loadout-router/SKILL.md +0 -120
- /package/dist/src/core/{routing → agents}/credentials.js +0 -0
|
@@ -1,539 +0,0 @@
|
|
|
1
|
-
// Sources:
|
|
2
|
-
// Anthropic: https://allaboutclaude.com/models
|
|
3
|
-
// OpenAI: https://benchlm.ai/openai/api-pricing
|
|
4
|
-
// Prices as of August 2026.
|
|
5
|
-
export const MODEL_CATALOG = [
|
|
6
|
-
// --- Anthropic (Claude Code) ---
|
|
7
|
-
{
|
|
8
|
-
id: "claude-fable-5",
|
|
9
|
-
provider: "anthropic",
|
|
10
|
-
name: "Claude Fable 5",
|
|
11
|
-
tier: "frontier",
|
|
12
|
-
inputCostPer1M: 10,
|
|
13
|
-
outputCostPer1M: 50,
|
|
14
|
-
nativeAgents: ["claude-code"],
|
|
15
|
-
current: true,
|
|
16
|
-
generation: "5",
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: "claude-opus-5",
|
|
20
|
-
provider: "anthropic",
|
|
21
|
-
name: "Claude Opus 5",
|
|
22
|
-
tier: "frontier",
|
|
23
|
-
inputCostPer1M: 5,
|
|
24
|
-
outputCostPer1M: 25,
|
|
25
|
-
nativeAgents: ["claude-code"],
|
|
26
|
-
current: true,
|
|
27
|
-
generation: "5",
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: "claude-sonnet-5",
|
|
31
|
-
provider: "anthropic",
|
|
32
|
-
name: "Claude Sonnet 5",
|
|
33
|
-
tier: "standard",
|
|
34
|
-
inputCostPer1M: 3,
|
|
35
|
-
outputCostPer1M: 15,
|
|
36
|
-
nativeAgents: ["claude-code"],
|
|
37
|
-
current: true,
|
|
38
|
-
generation: "5",
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
id: "claude-opus-4-8",
|
|
42
|
-
provider: "anthropic",
|
|
43
|
-
name: "Claude Opus 4.8",
|
|
44
|
-
tier: "frontier",
|
|
45
|
-
inputCostPer1M: 5,
|
|
46
|
-
outputCostPer1M: 25,
|
|
47
|
-
nativeAgents: ["claude-code"],
|
|
48
|
-
current: false,
|
|
49
|
-
generation: "4",
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: "claude-opus-4-6",
|
|
53
|
-
provider: "anthropic",
|
|
54
|
-
name: "Claude Opus 4.6",
|
|
55
|
-
tier: "frontier",
|
|
56
|
-
inputCostPer1M: 5,
|
|
57
|
-
outputCostPer1M: 25,
|
|
58
|
-
nativeAgents: ["claude-code"],
|
|
59
|
-
current: false,
|
|
60
|
-
generation: "4",
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
id: "claude-sonnet-4-6",
|
|
64
|
-
provider: "anthropic",
|
|
65
|
-
name: "Claude Sonnet 4.6",
|
|
66
|
-
tier: "standard",
|
|
67
|
-
inputCostPer1M: 3,
|
|
68
|
-
outputCostPer1M: 15,
|
|
69
|
-
nativeAgents: ["claude-code"],
|
|
70
|
-
current: false,
|
|
71
|
-
generation: "4",
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
id: "claude-haiku-4-5",
|
|
75
|
-
provider: "anthropic",
|
|
76
|
-
name: "Claude Haiku 4.5",
|
|
77
|
-
tier: "fast",
|
|
78
|
-
inputCostPer1M: 1,
|
|
79
|
-
outputCostPer1M: 5,
|
|
80
|
-
nativeAgents: ["claude-code"],
|
|
81
|
-
current: true,
|
|
82
|
-
generation: "4",
|
|
83
|
-
},
|
|
84
|
-
// --- OpenAI (Codex) — GPT-5.6 family ---
|
|
85
|
-
{
|
|
86
|
-
id: "gpt-5.6-sol",
|
|
87
|
-
provider: "openai",
|
|
88
|
-
name: "GPT-5.6 Sol",
|
|
89
|
-
tier: "frontier",
|
|
90
|
-
inputCostPer1M: 5,
|
|
91
|
-
outputCostPer1M: 30,
|
|
92
|
-
nativeAgents: ["codex"],
|
|
93
|
-
current: true,
|
|
94
|
-
generation: "5.6",
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
id: "gpt-5.6-terra",
|
|
98
|
-
provider: "openai",
|
|
99
|
-
name: "GPT-5.6 Terra",
|
|
100
|
-
tier: "standard",
|
|
101
|
-
inputCostPer1M: 2,
|
|
102
|
-
outputCostPer1M: 12,
|
|
103
|
-
nativeAgents: ["codex"],
|
|
104
|
-
current: true,
|
|
105
|
-
generation: "5.6",
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
id: "gpt-5.6-luna",
|
|
109
|
-
provider: "openai",
|
|
110
|
-
name: "GPT-5.6 Luna",
|
|
111
|
-
tier: "fast",
|
|
112
|
-
inputCostPer1M: 0.2,
|
|
113
|
-
outputCostPer1M: 1.2,
|
|
114
|
-
nativeAgents: ["codex"],
|
|
115
|
-
current: true,
|
|
116
|
-
generation: "5.6",
|
|
117
|
-
},
|
|
118
|
-
// --- OpenAI — GPT-5.5 ---
|
|
119
|
-
{
|
|
120
|
-
id: "gpt-5.5",
|
|
121
|
-
provider: "openai",
|
|
122
|
-
name: "GPT-5.5",
|
|
123
|
-
tier: "frontier",
|
|
124
|
-
inputCostPer1M: 5,
|
|
125
|
-
outputCostPer1M: 30,
|
|
126
|
-
nativeAgents: ["codex"],
|
|
127
|
-
current: true,
|
|
128
|
-
generation: "5.5",
|
|
129
|
-
},
|
|
130
|
-
// --- OpenAI — GPT-5.4 family ---
|
|
131
|
-
{
|
|
132
|
-
id: "gpt-5.4",
|
|
133
|
-
provider: "openai",
|
|
134
|
-
name: "GPT-5.4",
|
|
135
|
-
tier: "standard",
|
|
136
|
-
inputCostPer1M: 2.5,
|
|
137
|
-
outputCostPer1M: 15,
|
|
138
|
-
nativeAgents: ["codex"],
|
|
139
|
-
current: true,
|
|
140
|
-
generation: "5.4",
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
id: "gpt-5.4-mini",
|
|
144
|
-
provider: "openai",
|
|
145
|
-
name: "GPT-5.4 Mini",
|
|
146
|
-
tier: "fast",
|
|
147
|
-
inputCostPer1M: 0.75,
|
|
148
|
-
outputCostPer1M: 4.5,
|
|
149
|
-
nativeAgents: ["codex"],
|
|
150
|
-
current: true,
|
|
151
|
-
generation: "5.4",
|
|
152
|
-
},
|
|
153
|
-
// --- OpenAI — Codex-specific ---
|
|
154
|
-
{
|
|
155
|
-
id: "gpt-5.1-codex",
|
|
156
|
-
provider: "openai",
|
|
157
|
-
name: "GPT-5.1 Codex",
|
|
158
|
-
tier: "standard",
|
|
159
|
-
inputCostPer1M: 1.25,
|
|
160
|
-
outputCostPer1M: 10,
|
|
161
|
-
nativeAgents: ["codex"],
|
|
162
|
-
current: true,
|
|
163
|
-
generation: "5.1",
|
|
164
|
-
},
|
|
165
|
-
// --- OpenAI — o-series reasoning ---
|
|
166
|
-
{
|
|
167
|
-
id: "o3-pro",
|
|
168
|
-
provider: "openai",
|
|
169
|
-
name: "o3-pro",
|
|
170
|
-
tier: "frontier",
|
|
171
|
-
inputCostPer1M: 20,
|
|
172
|
-
outputCostPer1M: 80,
|
|
173
|
-
nativeAgents: ["codex"],
|
|
174
|
-
current: true,
|
|
175
|
-
generation: "o3",
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
id: "o3",
|
|
179
|
-
provider: "openai",
|
|
180
|
-
name: "o3",
|
|
181
|
-
tier: "frontier",
|
|
182
|
-
inputCostPer1M: 2,
|
|
183
|
-
outputCostPer1M: 8,
|
|
184
|
-
nativeAgents: ["codex"],
|
|
185
|
-
current: true,
|
|
186
|
-
generation: "o3",
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
id: "o4-mini",
|
|
190
|
-
provider: "openai",
|
|
191
|
-
name: "o4-mini",
|
|
192
|
-
tier: "standard",
|
|
193
|
-
inputCostPer1M: 1.1,
|
|
194
|
-
outputCostPer1M: 4.4,
|
|
195
|
-
nativeAgents: ["codex"],
|
|
196
|
-
current: true,
|
|
197
|
-
generation: "o4",
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
id: "o3-mini",
|
|
201
|
-
provider: "openai",
|
|
202
|
-
name: "o3-mini",
|
|
203
|
-
tier: "standard",
|
|
204
|
-
inputCostPer1M: 1.1,
|
|
205
|
-
outputCostPer1M: 4.4,
|
|
206
|
-
nativeAgents: ["codex"],
|
|
207
|
-
current: true,
|
|
208
|
-
generation: "o3",
|
|
209
|
-
},
|
|
210
|
-
];
|
|
211
|
-
const TIER_LABELS = {
|
|
212
|
-
frontier: "Frontier (deep reasoning)",
|
|
213
|
-
standard: "Standard (balanced)",
|
|
214
|
-
fast: "Fast (high throughput)",
|
|
215
|
-
};
|
|
216
|
-
const PHASE_CONFIG = {
|
|
217
|
-
plan: {
|
|
218
|
-
tier: "frontier",
|
|
219
|
-
reason: "Architecture and decomposition need deep reasoning to avoid costly rework",
|
|
220
|
-
agents: ["claude-code"],
|
|
221
|
-
conserveTier: "standard",
|
|
222
|
-
conserveTradeoff: "Slightly shallower architectural reasoning; review the plan more carefully",
|
|
223
|
-
},
|
|
224
|
-
implement: {
|
|
225
|
-
tier: "standard",
|
|
226
|
-
reason: "Implementation is high-volume; standard models score within 5% of frontier on SWE-bench",
|
|
227
|
-
agents: ["claude-code", "codex"],
|
|
228
|
-
conserveTier: "fast",
|
|
229
|
-
conserveTradeoff: "May need more iterations on complex logic; fine for CRUD and boilerplate",
|
|
230
|
-
},
|
|
231
|
-
review: {
|
|
232
|
-
tier: "frontier",
|
|
233
|
-
reason: "Code review catches subtle bugs that cheaper models miss; worth the cost per-review",
|
|
234
|
-
agents: ["claude-code", "codex"],
|
|
235
|
-
conserveTier: "standard",
|
|
236
|
-
conserveTradeoff: "May miss edge-case bugs; pair with a linter",
|
|
237
|
-
},
|
|
238
|
-
test: {
|
|
239
|
-
tier: "fast",
|
|
240
|
-
reason: "Test generation is pattern-heavy; fast models produce equivalent coverage",
|
|
241
|
-
agents: ["claude-code", "codex"],
|
|
242
|
-
},
|
|
243
|
-
debug: {
|
|
244
|
-
tier: "standard",
|
|
245
|
-
reason: "Targeted debugging needs good reasoning but not frontier-level; standard balances cost and quality",
|
|
246
|
-
agents: ["claude-code", "codex"],
|
|
247
|
-
conserveTier: "fast",
|
|
248
|
-
conserveTradeoff: "Works for simple bugs; complex root-cause analysis may suffer",
|
|
249
|
-
},
|
|
250
|
-
document: {
|
|
251
|
-
tier: "fast",
|
|
252
|
-
reason: "Documentation and comments are well-suited to fast models; any decent model writes docs",
|
|
253
|
-
agents: ["claude-code", "codex"],
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
const PHASE_KEYWORDS = {
|
|
257
|
-
plan: [
|
|
258
|
-
"plan",
|
|
259
|
-
"design",
|
|
260
|
-
"architect",
|
|
261
|
-
"architecture",
|
|
262
|
-
"rfc",
|
|
263
|
-
"spec",
|
|
264
|
-
"decompose",
|
|
265
|
-
"strategy",
|
|
266
|
-
"system design",
|
|
267
|
-
],
|
|
268
|
-
implement: [
|
|
269
|
-
"implement",
|
|
270
|
-
"build",
|
|
271
|
-
"code",
|
|
272
|
-
"create",
|
|
273
|
-
"add",
|
|
274
|
-
"feature",
|
|
275
|
-
"write",
|
|
276
|
-
"develop",
|
|
277
|
-
"make",
|
|
278
|
-
],
|
|
279
|
-
review: [
|
|
280
|
-
"review",
|
|
281
|
-
"audit",
|
|
282
|
-
"check",
|
|
283
|
-
"inspect",
|
|
284
|
-
"critique",
|
|
285
|
-
"pr",
|
|
286
|
-
"pull request",
|
|
287
|
-
"diff",
|
|
288
|
-
],
|
|
289
|
-
test: [
|
|
290
|
-
"test",
|
|
291
|
-
"spec",
|
|
292
|
-
"coverage",
|
|
293
|
-
"unit",
|
|
294
|
-
"integration",
|
|
295
|
-
"e2e",
|
|
296
|
-
"assert",
|
|
297
|
-
"vitest",
|
|
298
|
-
"jest",
|
|
299
|
-
],
|
|
300
|
-
debug: [
|
|
301
|
-
"debug",
|
|
302
|
-
"fix",
|
|
303
|
-
"bug",
|
|
304
|
-
"error",
|
|
305
|
-
"crash",
|
|
306
|
-
"broken",
|
|
307
|
-
"investigate",
|
|
308
|
-
"troubleshoot",
|
|
309
|
-
"failing",
|
|
310
|
-
],
|
|
311
|
-
document: [
|
|
312
|
-
"document",
|
|
313
|
-
"docs",
|
|
314
|
-
"readme",
|
|
315
|
-
"comment",
|
|
316
|
-
"jsdoc",
|
|
317
|
-
"changelog",
|
|
318
|
-
"explain",
|
|
319
|
-
"docstring",
|
|
320
|
-
],
|
|
321
|
-
};
|
|
322
|
-
// ---------------------------------------------------------------------------
|
|
323
|
-
// Classification
|
|
324
|
-
// ---------------------------------------------------------------------------
|
|
325
|
-
export function classifyTask(description) {
|
|
326
|
-
const lower = description.toLowerCase();
|
|
327
|
-
let best = "implement";
|
|
328
|
-
let bestScore = 0;
|
|
329
|
-
for (const [phase, keywords] of Object.entries(PHASE_KEYWORDS)) {
|
|
330
|
-
let score = 0;
|
|
331
|
-
for (const kw of keywords) {
|
|
332
|
-
if (lower.includes(kw))
|
|
333
|
-
score++;
|
|
334
|
-
}
|
|
335
|
-
if (score > bestScore) {
|
|
336
|
-
bestScore = score;
|
|
337
|
-
best = phase;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return best;
|
|
341
|
-
}
|
|
342
|
-
// ---------------------------------------------------------------------------
|
|
343
|
-
// Model queries
|
|
344
|
-
// ---------------------------------------------------------------------------
|
|
345
|
-
export function modelsForTier(tier, currentOnly = true) {
|
|
346
|
-
return MODEL_CATALOG.filter((m) => m.tier === tier && (!currentOnly || m.current));
|
|
347
|
-
}
|
|
348
|
-
export function modelsByProvider(provider) {
|
|
349
|
-
return MODEL_CATALOG.filter((m) => m.provider === provider && m.current);
|
|
350
|
-
}
|
|
351
|
-
export function cheapestInTier(tier) {
|
|
352
|
-
const models = modelsForTier(tier);
|
|
353
|
-
return models.sort((a, b) => a.inputCostPer1M - b.inputCostPer1M)[0];
|
|
354
|
-
}
|
|
355
|
-
/**
|
|
356
|
-
* Narrow a recommendation to the agents actually present on this machine. A
|
|
357
|
-
* recommendation that names an agent the user has not installed is noise, so
|
|
358
|
-
* the resolved list drives both the printed advice and the handoff command.
|
|
359
|
-
*/
|
|
360
|
-
export function resolveAvailableAgents(suggested, installed) {
|
|
361
|
-
const present = new Set(installed);
|
|
362
|
-
return {
|
|
363
|
-
available: suggested.filter((id) => present.has(id)),
|
|
364
|
-
missing: suggested.filter((id) => !present.has(id)),
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
/** Models in the recommended tier that the given agents can actually run. */
|
|
368
|
-
export function modelsRunnableBy(models, agents) {
|
|
369
|
-
if (!agents.length)
|
|
370
|
-
return models;
|
|
371
|
-
const wanted = new Set(agents);
|
|
372
|
-
return models.filter((m) => m.nativeAgents.some((id) => wanted.has(id)));
|
|
373
|
-
}
|
|
374
|
-
// ---------------------------------------------------------------------------
|
|
375
|
-
// Routing
|
|
376
|
-
// ---------------------------------------------------------------------------
|
|
377
|
-
export function routeTask(description, conserve = false) {
|
|
378
|
-
return routePhase(classifyTask(description), conserve);
|
|
379
|
-
}
|
|
380
|
-
export function routePhase(phase, conserve = false) {
|
|
381
|
-
const config = PHASE_CONFIG[phase];
|
|
382
|
-
if (!config)
|
|
383
|
-
throw new Error(`Unknown phase '${phase}'. Valid: ${Object.keys(PHASE_CONFIG).join(", ")}`);
|
|
384
|
-
const effectiveTier = conserve && config.conserveTier ? config.conserveTier : config.tier;
|
|
385
|
-
const models = modelsForTier(effectiveTier);
|
|
386
|
-
const rec = {
|
|
387
|
-
phase: phase,
|
|
388
|
-
tier: effectiveTier,
|
|
389
|
-
tierLabel: TIER_LABELS[effectiveTier],
|
|
390
|
-
models,
|
|
391
|
-
reason: conserve && config.conserveTier
|
|
392
|
-
? `Conserve mode: ${config.conserveTradeoff}`
|
|
393
|
-
: config.reason,
|
|
394
|
-
suggestedAgents: config.agents,
|
|
395
|
-
};
|
|
396
|
-
if (!conserve && config.conserveTier) {
|
|
397
|
-
rec.conserveAlternative = {
|
|
398
|
-
tier: config.conserveTier,
|
|
399
|
-
models: modelsForTier(config.conserveTier),
|
|
400
|
-
tradeoff: config.conserveTradeoff,
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
return rec;
|
|
404
|
-
}
|
|
405
|
-
export function allPhaseRoutes(conserve = false) {
|
|
406
|
-
return Object.keys(PHASE_CONFIG).map((p) => routePhase(p, conserve));
|
|
407
|
-
}
|
|
408
|
-
export function estimateCostSavings() {
|
|
409
|
-
const normal = allPhaseRoutes(false);
|
|
410
|
-
const conserved = allPhaseRoutes(true);
|
|
411
|
-
const estimates = normal.map((r) => {
|
|
412
|
-
const cheapest = cheapestInTier(r.tier);
|
|
413
|
-
return {
|
|
414
|
-
phase: r.phase,
|
|
415
|
-
tier: r.tier,
|
|
416
|
-
cheapestModel: cheapest?.name ?? "—",
|
|
417
|
-
inputCost: cheapest?.inputCostPer1M ?? 0,
|
|
418
|
-
outputCost: cheapest?.outputCostPer1M ?? 0,
|
|
419
|
-
};
|
|
420
|
-
});
|
|
421
|
-
const normalTotal = estimates.reduce((s, e) => s + e.inputCost, 0);
|
|
422
|
-
const conserveEstimates = conserved.map((r) => cheapestInTier(r.tier)?.inputCostPer1M ?? 0);
|
|
423
|
-
const conserveTotal = conserveEstimates.reduce((s, c) => s + c, 0);
|
|
424
|
-
return { estimates, normalTotal, conserveTotal };
|
|
425
|
-
}
|
|
426
|
-
/** Shell-quote a task description for the suggested handoff command. */
|
|
427
|
-
function shellQuote(value) {
|
|
428
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
429
|
-
}
|
|
430
|
-
export function formatRouteRecommendation(rec, context = {}) {
|
|
431
|
-
// `undefined` means detection never ran, so every suggested agent stands. An
|
|
432
|
-
// empty array means detection ran and found nothing, which must not be
|
|
433
|
-
// silently upgraded back to the full suggestion list.
|
|
434
|
-
const checked = context.installedAgents !== undefined;
|
|
435
|
-
const { available, missing } = checked
|
|
436
|
-
? resolveAvailableAgents(rec.suggestedAgents, context.installedAgents)
|
|
437
|
-
: { available: rec.suggestedAgents, missing: [] };
|
|
438
|
-
// Only recommend models an available agent can actually run.
|
|
439
|
-
const runnable = checked
|
|
440
|
-
? modelsRunnableBy(rec.models, available)
|
|
441
|
-
: rec.models;
|
|
442
|
-
const shown = (runnable.length ? runnable : rec.models).slice(0, 4);
|
|
443
|
-
const modelNames = shown.map((m) => `${m.name} ($${m.inputCostPer1M}/$${m.outputCostPer1M})`);
|
|
444
|
-
const lines = [
|
|
445
|
-
`Phase: ${rec.phase}`,
|
|
446
|
-
`Tier: ${rec.tierLabel}`,
|
|
447
|
-
`Models: ${modelNames.join("\n ")}`,
|
|
448
|
-
`Agents: ${available.length ? available.join(", ") : "none detected"}${missing.length ? ` (not installed: ${missing.join(", ")})` : ""}`,
|
|
449
|
-
`Why: ${rec.reason}`,
|
|
450
|
-
];
|
|
451
|
-
if (rec.conserveAlternative) {
|
|
452
|
-
const alt = rec.conserveAlternative;
|
|
453
|
-
const altCheapest = cheapestInTier(alt.tier);
|
|
454
|
-
lines.push(``, `Conserve: drop to ${alt.tier} tier (${altCheapest?.name ?? "—"} at $${altCheapest?.inputCostPer1M ?? "?"}/$${altCheapest?.outputCostPer1M ?? "?"})`, ` ${alt.tradeoff}`);
|
|
455
|
-
}
|
|
456
|
-
// Actionable next step: hand this task to a detected agent.
|
|
457
|
-
if (context.description && available.length) {
|
|
458
|
-
const target = available[0];
|
|
459
|
-
lines.push(``, `Hand off:`, ` loadout handoff send ${target} ${shellQuote(context.description)}`);
|
|
460
|
-
if (context.handoffReady === false)
|
|
461
|
-
lines.push(` (run \`loadout handoff init\` once to enable)`);
|
|
462
|
-
}
|
|
463
|
-
return lines.join("\n");
|
|
464
|
-
}
|
|
465
|
-
export function formatRoutingTable(conserve = false) {
|
|
466
|
-
const routes = allPhaseRoutes(conserve);
|
|
467
|
-
const header = `Phase Tier Cheapest model $/M in $/M out Agents`;
|
|
468
|
-
const divider = "─".repeat(header.length);
|
|
469
|
-
const rows = routes.map((r) => {
|
|
470
|
-
const cheapest = cheapestInTier(r.tier);
|
|
471
|
-
const phase = r.phase.padEnd(12);
|
|
472
|
-
const tier = r.tier.padEnd(11);
|
|
473
|
-
const model = (cheapest?.name ?? "—").padEnd(27);
|
|
474
|
-
const inCost = `$${(cheapest?.inputCostPer1M ?? 0).toFixed(2)}`.padEnd(8);
|
|
475
|
-
const outCost = `$${(cheapest?.outputCostPer1M ?? 0).toFixed(2)}`.padEnd(8);
|
|
476
|
-
const agents = r.suggestedAgents.slice(0, 3).join(", ");
|
|
477
|
-
return `${phase} ${tier} ${model} ${inCost} ${outCost} ${agents}`;
|
|
478
|
-
});
|
|
479
|
-
return [
|
|
480
|
-
conserve
|
|
481
|
-
? "ROUTING TABLE (conserve mode — usage-saving tier for each phase)"
|
|
482
|
-
: "ROUTING TABLE",
|
|
483
|
-
"",
|
|
484
|
-
header,
|
|
485
|
-
divider,
|
|
486
|
-
...rows,
|
|
487
|
-
].join("\n");
|
|
488
|
-
}
|
|
489
|
-
export function formatCostTable() {
|
|
490
|
-
const { estimates, normalTotal, conserveTotal } = estimateCostSavings();
|
|
491
|
-
const header = "Phase Tier Cheapest model $/M input";
|
|
492
|
-
const divider = "─".repeat(header.length);
|
|
493
|
-
const rows = estimates.map((e) => {
|
|
494
|
-
const phase = e.phase.padEnd(12);
|
|
495
|
-
const tier = e.tier.padEnd(11);
|
|
496
|
-
const model = e.cheapestModel.padEnd(27);
|
|
497
|
-
return `${phase} ${tier} ${model} $${e.inputCost.toFixed(2)}`;
|
|
498
|
-
});
|
|
499
|
-
const savings = Math.round((1 - conserveTotal / normalTotal) * 100);
|
|
500
|
-
return [
|
|
501
|
-
header,
|
|
502
|
-
divider,
|
|
503
|
-
...rows,
|
|
504
|
-
divider,
|
|
505
|
-
`Normal total: $${normalTotal.toFixed(2)}/M input across 6 phases`,
|
|
506
|
-
`Conserve total: $${conserveTotal.toFixed(2)}/M input (${savings}% cheaper)`,
|
|
507
|
-
"",
|
|
508
|
-
"Prices are per-million-token list rates. Actual cost depends on prompt length.",
|
|
509
|
-
"Use --conserve when you want to stretch remaining session quota.",
|
|
510
|
-
].join("\n");
|
|
511
|
-
}
|
|
512
|
-
export function formatModelCatalog(filter) {
|
|
513
|
-
let models = MODEL_CATALOG;
|
|
514
|
-
if (filter?.provider)
|
|
515
|
-
models = models.filter((m) => m.provider === filter.provider);
|
|
516
|
-
if (filter?.tier)
|
|
517
|
-
models = models.filter((m) => m.tier === filter.tier);
|
|
518
|
-
if (filter?.current !== undefined)
|
|
519
|
-
models = models.filter((m) => m.current === filter.current);
|
|
520
|
-
const header = "Model Provider Tier $/M in $/M out Gen Agents";
|
|
521
|
-
const divider = "─".repeat(header.length);
|
|
522
|
-
const rows = models.map((m) => {
|
|
523
|
-
const name = m.name.padEnd(28);
|
|
524
|
-
const provider = m.provider.padEnd(11);
|
|
525
|
-
const tier = m.tier.padEnd(11);
|
|
526
|
-
const inCost = `$${m.inputCostPer1M.toFixed(2)}`.padEnd(8);
|
|
527
|
-
const outCost = `$${m.outputCostPer1M.toFixed(2)}`.padEnd(8);
|
|
528
|
-
const gen = m.generation.padEnd(6);
|
|
529
|
-
const agents = m.nativeAgents.slice(0, 3).join(", ");
|
|
530
|
-
return `${name} ${provider} ${tier} ${inCost} ${outCost} ${gen} ${agents}`;
|
|
531
|
-
});
|
|
532
|
-
return [
|
|
533
|
-
`${models.length} models${filter?.current === false ? " (including legacy)" : ""}`,
|
|
534
|
-
"",
|
|
535
|
-
header,
|
|
536
|
-
divider,
|
|
537
|
-
...rows,
|
|
538
|
-
].join("\n");
|
|
539
|
-
}
|
package/docs/ACTIVE_SET.md
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
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 30
|
|
49
|
-
loadout optimize --project .
|
|
50
|
-
loadout rollback --snapshot <id>
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Machine-readable planning and results are available with `--json`.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Compatibility policy
|
|
2
|
-
|
|
3
|
-
Loadout uses the same adapter matrix in inspection and installation. Each
|
|
4
|
-
agent/component cell is one of:
|
|
5
|
-
|
|
6
|
-
- **native** — Loadout writes the documented component format to the
|
|
7
|
-
agent-owned location.
|
|
8
|
-
- **adapted** — Loadout has a reviewed, tested mapping to an agent-specific
|
|
9
|
-
layout or explicit config writer. The plan warns about the adaptation.
|
|
10
|
-
- **unsupported** — Loadout does not infer a location, convert the component,
|
|
11
|
-
or write anything.
|
|
12
|
-
|
|
13
|
-
All supported platforms (Windows, macOS, and Linux) use this policy. WSL is
|
|
14
|
-
Linux-side only: Loadout manages the POSIX home directory and never silently
|
|
15
|
-
rewrites a Windows profile. Agent adapters are deliberately conservative:
|
|
16
|
-
skills are native wherever a documented skill directory exists; commands,
|
|
17
|
-
rules, and agents are native only where the adapter supplies a concrete
|
|
18
|
-
directory; MCP is config-scoped and adapted only through its explicit JSON or
|
|
19
|
-
Codex TOML planner; and plugin manifests are inspected/normalized but their
|
|
20
|
-
runtime hooks are never converted or executed.
|
|
21
|
-
|
|
22
|
-
Use `loadout capabilities --inspect` to view the matrix and local inventory.
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Conversion loss reports and installer sandbox boundary
|
|
2
|
-
|
|
3
|
-
## Cross-agent conversion
|
|
4
|
-
|
|
5
|
-
The only safe initial conversion target is a static instruction file. A source skill
|
|
6
|
-
can be copied to an agent's documented native skill directory when that adapter marks
|
|
7
|
-
skills `native`. For a target that supports only commands, Loadout may create one
|
|
8
|
-
explicit command wrapper pointing at the same static instructions.
|
|
9
|
-
|
|
10
|
-
Every conversion emits a loss report containing source component, target component,
|
|
11
|
-
preserved fields, dropped fields, and the reason. Hooks, subagents, lifecycle scripts,
|
|
12
|
-
dynamic variables, permissions, and MCP servers are never silently converted. A loss
|
|
13
|
-
report with dropped executable behavior requires user approval and defaults to no
|
|
14
|
-
write.
|
|
15
|
-
|
|
16
|
-
## Third-party installer threat model
|
|
17
|
-
|
|
18
|
-
An installer can read secrets, write outside its target, alter shell startup files,
|
|
19
|
-
open network connections, launch processes, or exploit the host. Therefore source
|
|
20
|
-
repository scripts are never run on the host. Static package parsing is the normal
|
|
21
|
-
path.
|
|
22
|
-
|
|
23
|
-
If a future disposable sandbox runner is enabled, it must use a fresh filesystem,
|
|
24
|
-
read-only source mount, no host home mount, no inherited environment variables, no
|
|
25
|
-
Docker socket, restricted network disabled by default, bounded CPU/memory/time, and
|
|
26
|
-
an exported file manifest. Its output is an untrusted proposal that still goes through
|
|
27
|
-
the normal plan/safety/approval path. Failure or timeout discards the sandbox.
|