loadout-ai 0.8.0 → 0.9.1

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +149 -309
  3. package/SECURITY.md +21 -1
  4. package/catalog/discovered.json +29122 -25325
  5. package/catalog/packages.json +4 -4
  6. package/dist/src/cli.js +12 -4
  7. package/dist/src/commands/agents.js +3 -1
  8. package/dist/src/commands/catalog-candidate.js +150 -0
  9. package/dist/src/commands/catalog-workflows.js +365 -0
  10. package/dist/src/commands/catalog.js +9 -354
  11. package/dist/src/commands/coordinate.js +730 -0
  12. package/dist/src/commands/coordination-discussions.js +218 -0
  13. package/dist/src/commands/coordination-sessions.js +197 -0
  14. package/dist/src/commands/inventory.js +4 -2
  15. package/dist/src/core/{routing → agents}/model-config.js +7 -2
  16. package/dist/src/core/catalog/catalog.js +1 -0
  17. package/dist/src/core/catalog/registry.js +5 -19
  18. package/dist/src/core/coordination/adapters/claude-code.js +154 -0
  19. package/dist/src/core/coordination/adapters/codex.js +160 -0
  20. package/dist/src/core/coordination/adapters/types.js +64 -0
  21. package/dist/src/core/coordination/auth.js +59 -0
  22. package/dist/src/core/coordination/auto-contract.js +457 -0
  23. package/dist/src/core/coordination/bridge-lease.js +79 -0
  24. package/dist/src/core/coordination/conflict-preview.js +167 -0
  25. package/dist/src/core/coordination/contract-diff.js +106 -0
  26. package/dist/src/core/coordination/coordinator.js +552 -0
  27. package/dist/src/core/coordination/crash-recovery.js +169 -0
  28. package/dist/src/core/coordination/daemon.js +670 -0
  29. package/dist/src/core/coordination/discussion-pipeline.js +313 -0
  30. package/dist/src/core/coordination/discussion.js +340 -0
  31. package/dist/src/core/coordination/events.js +161 -0
  32. package/dist/src/core/coordination/git-ownership.js +217 -0
  33. package/dist/src/core/coordination/http-api.js +118 -0
  34. package/dist/src/core/coordination/interrupt-policy.js +89 -0
  35. package/dist/src/core/coordination/lock.js +140 -0
  36. package/dist/src/core/coordination/mcp-server.js +424 -0
  37. package/dist/src/core/coordination/quick-start.js +200 -0
  38. package/dist/src/core/coordination/redaction.js +85 -0
  39. package/dist/src/core/coordination/replay.js +188 -0
  40. package/dist/src/core/coordination/retention.js +180 -0
  41. package/dist/src/core/coordination/runtime.js +21 -0
  42. package/dist/src/core/coordination/session-manager.js +366 -0
  43. package/dist/src/core/coordination/watcher.js +128 -0
  44. package/dist/src/core/{routing → delegation}/first-party-skills.js +25 -13
  45. package/dist/src/core/delegation/handoff-bundle.js +253 -0
  46. package/dist/src/core/delegation/handoff-templates.js +222 -0
  47. package/dist/src/core/delegation/handoff-verification.js +117 -0
  48. package/dist/src/core/delegation/handoff.js +537 -0
  49. package/dist/src/core/discovery/candidate-intelligence-evidence.js +108 -0
  50. package/dist/src/core/discovery/candidate-intelligence-types.js +1 -0
  51. package/dist/src/core/discovery/candidate-intelligence-validation.js +108 -0
  52. package/dist/src/core/discovery/candidate-intelligence.js +3 -214
  53. package/dist/src/core/discovery/community.js +12 -4
  54. package/dist/src/core/discovery/github-discovery.js +6 -2
  55. package/dist/src/core/discovery/private-discovery.js +6 -2
  56. package/dist/src/core/install/reconcile.js +1 -1
  57. package/dist/src/core/install/snapshot.js +24 -5
  58. package/dist/src/core/install/uninstall.js +39 -3
  59. package/dist/src/core/reporting/cli-guide.js +9 -5
  60. package/dist/src/core/reporting/completion.js +43 -38
  61. package/dist/src/core/reporting/doctor.js +1 -1
  62. package/dist/src/core/runtime/bounded-json.js +65 -0
  63. package/dist/src/core/runtime/github.js +30 -16
  64. package/dist/src/core/runtime/mcp-recipes.js +1 -1
  65. package/dist/src/core/workspace/active-policy.js +1 -1
  66. package/docs/DEMO_SCRIPT.md +19 -23
  67. package/docs/DISCOVERED.md +250 -251
  68. package/docs/FEATURE_TEST_MATRIX.md +30 -15
  69. package/docs/LIVE_COLLABORATION.md +283 -0
  70. package/docs/REFERENCE.md +263 -0
  71. package/docs/RELEASE_REVIEW.md +1 -1
  72. package/docs/TESTING.md +17 -1
  73. package/docs/USER_TEST_GUIDE.md +211 -3
  74. package/docs/assets/loadout-discover-activate.webp +0 -0
  75. package/docs/assets/loadout-handoff-coordinate.webp +0 -0
  76. package/docs/assets/loadout-social-preview.png +0 -0
  77. package/docs/decisions/001-coordination-jsonl-locking.md +35 -0
  78. package/docs/decisions/002-local-daemon-authentication.md +32 -0
  79. package/docs/decisions/003-bounded-agent-discussions.md +91 -0
  80. package/docs/evidence/coordination-provider-check-2026-09-05.md +33 -0
  81. package/docs/specs/BOUNDED_AGENT_DISCUSSIONS.md +169 -0
  82. package/docs/specs/HANDOFF_CONTEXT_BUNDLES.md +139 -0
  83. package/docs/specs/HANDOFF_VERIFICATION.md +83 -0
  84. package/docs/superpowers/plans/2026-09-03-coordination-hardening.md +231 -0
  85. package/docs/superpowers/plans/2026-09-03-release-readiness.md +232 -0
  86. package/docs/superpowers/plans/2026-09-04-bounded-agent-discussions.md +121 -0
  87. package/docs/superpowers/plans/2026-09-04-handoff-context-bundles.md +109 -0
  88. package/docs/superpowers/plans/2026-09-04-handoff-verification.md +56 -0
  89. package/docs/superpowers/plans/2026-09-05-pre-release-hardening.md +175 -0
  90. package/docs/superpowers/plans/2026-09-05-public-readiness.md +20 -0
  91. package/package.json +15 -5
  92. package/skills/loadout-handoff/SKILL.md +291 -0
  93. package/dist/src/core/routing/handoff.js +0 -314
  94. package/dist/src/core/routing/policy.js +0 -147
  95. package/dist/src/core/routing/route.js +0 -411
  96. package/docs/assets/loadout-workflow.png +0 -0
  97. package/skills/loadout-router/SKILL.md +0 -85
  98. /package/dist/src/core/{routing → agents}/credentials.js +0 -0
@@ -1,411 +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
- // Haiku and the 4.x line are deliberately absent: for real coding work the
8
- // choice is Opus or Sonnet, and offering a weaker tier invites picking it.
9
- {
10
- id: "claude-opus-5",
11
- provider: "anthropic",
12
- name: "Claude Opus 5",
13
- tier: "frontier",
14
- inputCostPer1M: 5,
15
- outputCostPer1M: 25,
16
- nativeAgents: ["claude-code"],
17
- current: true,
18
- generation: "5",
19
- },
20
- {
21
- id: "claude-sonnet-5",
22
- provider: "anthropic",
23
- name: "Claude Sonnet 5",
24
- tier: "standard",
25
- inputCostPer1M: 3,
26
- outputCostPer1M: 15,
27
- nativeAgents: ["claude-code"],
28
- current: true,
29
- generation: "5",
30
- },
31
- // --- OpenAI (Codex) — the current GPT-5.6 tiers only ---
32
- {
33
- id: "gpt-5.6-sol",
34
- provider: "openai",
35
- name: "GPT-5.6 Sol",
36
- tier: "frontier",
37
- inputCostPer1M: 5,
38
- outputCostPer1M: 30,
39
- nativeAgents: ["codex"],
40
- current: true,
41
- generation: "5.6",
42
- },
43
- {
44
- id: "gpt-5.6-terra",
45
- provider: "openai",
46
- name: "GPT-5.6 Terra",
47
- tier: "standard",
48
- inputCostPer1M: 2,
49
- outputCostPer1M: 12,
50
- nativeAgents: ["codex"],
51
- current: true,
52
- generation: "5.6",
53
- },
54
- {
55
- id: "gpt-5.6-luna",
56
- provider: "openai",
57
- name: "GPT-5.6 Luna",
58
- tier: "fast",
59
- inputCostPer1M: 0.2,
60
- outputCostPer1M: 1.2,
61
- nativeAgents: ["codex"],
62
- current: true,
63
- generation: "5.6",
64
- },
65
- ];
66
- const TIER_LABELS = {
67
- frontier: "Frontier (deep reasoning)",
68
- standard: "Standard (balanced)",
69
- fast: "Fast (high throughput)",
70
- };
71
- const PHASE_CONFIG = {
72
- plan: {
73
- tier: "frontier",
74
- reason: "Architecture and decomposition need deep reasoning to avoid costly rework",
75
- agents: ["claude-code"],
76
- conserveTier: "standard",
77
- conserveTradeoff: "Slightly shallower architectural reasoning; review the plan more carefully",
78
- },
79
- implement: {
80
- tier: "standard",
81
- reason: "Implementation is high-volume; standard models score within 5% of frontier on SWE-bench",
82
- agents: ["claude-code", "codex"],
83
- conserveTier: "fast",
84
- conserveTradeoff: "May need more iterations on complex logic; fine for CRUD and boilerplate",
85
- },
86
- review: {
87
- tier: "frontier",
88
- reason: "Code review catches subtle bugs that cheaper models miss; worth the cost per-review",
89
- agents: ["claude-code", "codex"],
90
- conserveTier: "standard",
91
- conserveTradeoff: "May miss edge-case bugs; pair with a linter",
92
- },
93
- test: {
94
- tier: "fast",
95
- reason: "Test generation is pattern-heavy; fast models produce equivalent coverage",
96
- agents: ["claude-code", "codex"],
97
- },
98
- debug: {
99
- tier: "standard",
100
- reason: "Targeted debugging needs good reasoning but not frontier-level; standard balances cost and quality",
101
- agents: ["claude-code", "codex"],
102
- conserveTier: "fast",
103
- conserveTradeoff: "Works for simple bugs; complex root-cause analysis may suffer",
104
- },
105
- document: {
106
- tier: "fast",
107
- reason: "Documentation and comments are well-suited to fast models; any decent model writes docs",
108
- agents: ["claude-code", "codex"],
109
- },
110
- };
111
- const PHASE_KEYWORDS = {
112
- plan: [
113
- "plan",
114
- "design",
115
- "architect",
116
- "architecture",
117
- "rfc",
118
- "spec",
119
- "decompose",
120
- "strategy",
121
- "system design",
122
- ],
123
- implement: [
124
- "implement",
125
- "build",
126
- "code",
127
- "create",
128
- "add",
129
- "feature",
130
- "write",
131
- "develop",
132
- "make",
133
- ],
134
- review: [
135
- "review",
136
- "audit",
137
- "check",
138
- "inspect",
139
- "critique",
140
- "pr",
141
- "pull request",
142
- "diff",
143
- ],
144
- test: [
145
- "test",
146
- "spec",
147
- "coverage",
148
- "unit",
149
- "integration",
150
- "e2e",
151
- "assert",
152
- "vitest",
153
- "jest",
154
- ],
155
- debug: [
156
- "debug",
157
- "fix",
158
- "bug",
159
- "error",
160
- "crash",
161
- "broken",
162
- "investigate",
163
- "troubleshoot",
164
- "failing",
165
- ],
166
- document: [
167
- "document",
168
- "docs",
169
- "readme",
170
- "comment",
171
- "jsdoc",
172
- "changelog",
173
- "explain",
174
- "docstring",
175
- ],
176
- };
177
- // ---------------------------------------------------------------------------
178
- // Classification
179
- // ---------------------------------------------------------------------------
180
- export function classifyTask(description) {
181
- const lower = description.toLowerCase();
182
- let best = "implement";
183
- let bestScore = 0;
184
- for (const [phase, keywords] of Object.entries(PHASE_KEYWORDS)) {
185
- let score = 0;
186
- for (const kw of keywords) {
187
- if (lower.includes(kw))
188
- score++;
189
- }
190
- if (score > bestScore) {
191
- bestScore = score;
192
- best = phase;
193
- }
194
- }
195
- return best;
196
- }
197
- // ---------------------------------------------------------------------------
198
- // Model queries
199
- // ---------------------------------------------------------------------------
200
- export function modelsForTier(tier, currentOnly = true) {
201
- return MODEL_CATALOG.filter((m) => m.tier === tier && (!currentOnly || m.current));
202
- }
203
- export function modelsByProvider(provider) {
204
- return MODEL_CATALOG.filter((m) => m.provider === provider && m.current);
205
- }
206
- export function cheapestInTier(tier) {
207
- const models = modelsForTier(tier);
208
- return models.sort((a, b) => a.inputCostPer1M - b.inputCostPer1M)[0];
209
- }
210
- /**
211
- * Narrow a recommendation to the agents actually present on this machine. A
212
- * recommendation that names an agent the user has not installed is noise, so
213
- * the resolved list drives both the printed advice and the handoff command.
214
- */
215
- export function resolveAvailableAgents(suggested, installed) {
216
- const present = new Set(installed);
217
- return {
218
- available: suggested.filter((id) => present.has(id)),
219
- missing: suggested.filter((id) => !present.has(id)),
220
- };
221
- }
222
- /** Models in the recommended tier that the given agents can actually run. */
223
- export function modelsRunnableBy(models, agents) {
224
- if (!agents.length)
225
- return models;
226
- const wanted = new Set(agents);
227
- return models.filter((m) => m.nativeAgents.some((id) => wanted.has(id)));
228
- }
229
- // ---------------------------------------------------------------------------
230
- // Routing
231
- // ---------------------------------------------------------------------------
232
- export function routeTask(description, conserve = false) {
233
- return routePhase(classifyTask(description), conserve);
234
- }
235
- export function routePhase(phase, conserve = false) {
236
- const config = PHASE_CONFIG[phase];
237
- if (!config)
238
- throw new Error(`Unknown phase '${phase}'. Valid: ${Object.keys(PHASE_CONFIG).join(", ")}`);
239
- const effectiveTier = conserve && config.conserveTier ? config.conserveTier : config.tier;
240
- const models = modelsForTier(effectiveTier);
241
- const rec = {
242
- phase: phase,
243
- tier: effectiveTier,
244
- tierLabel: TIER_LABELS[effectiveTier],
245
- models,
246
- reason: conserve && config.conserveTier
247
- ? `Conserve mode: ${config.conserveTradeoff}`
248
- : config.reason,
249
- suggestedAgents: config.agents,
250
- };
251
- if (!conserve && config.conserveTier) {
252
- rec.conserveAlternative = {
253
- tier: config.conserveTier,
254
- models: modelsForTier(config.conserveTier),
255
- tradeoff: config.conserveTradeoff,
256
- };
257
- }
258
- return rec;
259
- }
260
- export function allPhaseRoutes(conserve = false) {
261
- return Object.keys(PHASE_CONFIG).map((p) => routePhase(p, conserve));
262
- }
263
- export function estimateCostSavings() {
264
- const normal = allPhaseRoutes(false);
265
- const conserved = allPhaseRoutes(true);
266
- const estimates = normal.map((r) => {
267
- const cheapest = cheapestInTier(r.tier);
268
- return {
269
- phase: r.phase,
270
- tier: r.tier,
271
- cheapestModel: cheapest?.name ?? "—",
272
- inputCost: cheapest?.inputCostPer1M ?? 0,
273
- outputCost: cheapest?.outputCostPer1M ?? 0,
274
- };
275
- });
276
- const normalTotal = estimates.reduce((s, e) => s + e.inputCost, 0);
277
- const conserveEstimates = conserved.map((r) => cheapestInTier(r.tier)?.inputCostPer1M ?? 0);
278
- const conserveTotal = conserveEstimates.reduce((s, c) => s + c, 0);
279
- return { estimates, normalTotal, conserveTotal };
280
- }
281
- /** Shell-quote a task description for the suggested handoff command. */
282
- function shellQuote(value) {
283
- return `'${value.replace(/'/g, `'\\''`)}'`;
284
- }
285
- export function formatRouteRecommendation(rec, context = {}) {
286
- // `undefined` means detection never ran, so every suggested agent stands. An
287
- // empty array means detection ran and found nothing, which must not be
288
- // silently upgraded back to the full suggestion list.
289
- const checked = context.installedAgents !== undefined;
290
- const { available, missing } = checked
291
- ? resolveAvailableAgents(rec.suggestedAgents, context.installedAgents)
292
- : { available: rec.suggestedAgents, missing: [] };
293
- // Only recommend models an available agent can actually run. Not every
294
- // provider offers every tier — Claude Code has no fast tier — so when the
295
- // recommended tier holds nothing the user can reach, step up to the nearest
296
- // tier that does rather than naming a model they cannot run.
297
- const runnable = checked
298
- ? modelsRunnableBy(rec.models, available)
299
- : rec.models;
300
- const stepUp = {
301
- fast: "standard",
302
- standard: "frontier",
303
- };
304
- let effective = runnable;
305
- let tier = rec.tier;
306
- let substituted;
307
- while (checked && available.length && !effective.length && stepUp[tier]) {
308
- const next = stepUp[tier];
309
- effective = modelsRunnableBy(modelsForTier(next), available);
310
- if (effective.length)
311
- substituted = { from: rec.tier, to: next };
312
- tier = next;
313
- }
314
- const shown = (effective.length ? effective : rec.models).slice(0, 4);
315
- const modelNames = shown.map((m) => `${m.name} ($${m.inputCostPer1M}/$${m.outputCostPer1M})`);
316
- const lines = [
317
- `Phase: ${rec.phase}`,
318
- `Tier: ${rec.tierLabel}`,
319
- `Models: ${modelNames.join("\n ")}`,
320
- `Agents: ${available.length ? available.join(", ") : "none detected"}${missing.length ? ` (not installed: ${missing.join(", ")})` : ""}`,
321
- `Why: ${rec.reason}`,
322
- ];
323
- if (substituted)
324
- lines.push(`Note: your agents have no ${substituted.from}-tier model, so this shows`, ` the ${substituted.to} tier instead.`);
325
- if (rec.conserveAlternative) {
326
- const alt = rec.conserveAlternative;
327
- const altCheapest = cheapestInTier(alt.tier);
328
- lines.push(``, `Conserve: drop to ${alt.tier} tier (${altCheapest?.name ?? "—"} at $${altCheapest?.inputCostPer1M ?? "?"}/$${altCheapest?.outputCostPer1M ?? "?"})`, ` ${alt.tradeoff}`);
329
- }
330
- // Actionable next step: hand this task to a detected agent.
331
- if (context.description && available.length) {
332
- const target = available[0];
333
- lines.push(``, `Hand off:`, ` loadout handoff ${target} ${shellQuote(context.description)}`);
334
- }
335
- return lines.join("\n");
336
- }
337
- export function formatRoutingTable(conserve = false) {
338
- const routes = allPhaseRoutes(conserve);
339
- const header = `Phase Tier Cheapest model $/M in $/M out Agents`;
340
- const divider = "─".repeat(header.length);
341
- const rows = routes.map((r) => {
342
- const cheapest = cheapestInTier(r.tier);
343
- const phase = r.phase.padEnd(12);
344
- const tier = r.tier.padEnd(11);
345
- const model = (cheapest?.name ?? "—").padEnd(27);
346
- const inCost = `$${(cheapest?.inputCostPer1M ?? 0).toFixed(2)}`.padEnd(8);
347
- const outCost = `$${(cheapest?.outputCostPer1M ?? 0).toFixed(2)}`.padEnd(8);
348
- const agents = r.suggestedAgents.slice(0, 3).join(", ");
349
- return `${phase} ${tier} ${model} ${inCost} ${outCost} ${agents}`;
350
- });
351
- return [
352
- conserve
353
- ? "ROUTING TABLE (conserve mode — usage-saving tier for each phase)"
354
- : "ROUTING TABLE",
355
- "",
356
- header,
357
- divider,
358
- ...rows,
359
- ].join("\n");
360
- }
361
- export function formatCostTable() {
362
- const { estimates, normalTotal, conserveTotal } = estimateCostSavings();
363
- const header = "Phase Tier Cheapest model $/M input";
364
- const divider = "─".repeat(header.length);
365
- const rows = estimates.map((e) => {
366
- const phase = e.phase.padEnd(12);
367
- const tier = e.tier.padEnd(11);
368
- const model = e.cheapestModel.padEnd(27);
369
- return `${phase} ${tier} ${model} $${e.inputCost.toFixed(2)}`;
370
- });
371
- const savings = Math.round((1 - conserveTotal / normalTotal) * 100);
372
- return [
373
- header,
374
- divider,
375
- ...rows,
376
- divider,
377
- `Normal total: $${normalTotal.toFixed(2)}/M input across 6 phases`,
378
- `Conserve total: $${conserveTotal.toFixed(2)}/M input (${savings}% cheaper)`,
379
- "",
380
- "Prices are per-million-token list rates. Actual cost depends on prompt length.",
381
- "Use --conserve when you want to stretch remaining session quota.",
382
- ].join("\n");
383
- }
384
- export function formatModelCatalog(filter) {
385
- let models = MODEL_CATALOG;
386
- if (filter?.provider)
387
- models = models.filter((m) => m.provider === filter.provider);
388
- if (filter?.tier)
389
- models = models.filter((m) => m.tier === filter.tier);
390
- if (filter?.current !== undefined)
391
- models = models.filter((m) => m.current === filter.current);
392
- const header = "Model Provider Tier $/M in $/M out Gen Agents";
393
- const divider = "─".repeat(header.length);
394
- const rows = models.map((m) => {
395
- const name = m.name.padEnd(28);
396
- const provider = m.provider.padEnd(11);
397
- const tier = m.tier.padEnd(11);
398
- const inCost = `$${m.inputCostPer1M.toFixed(2)}`.padEnd(8);
399
- const outCost = `$${m.outputCostPer1M.toFixed(2)}`.padEnd(8);
400
- const gen = m.generation.padEnd(6);
401
- const agents = m.nativeAgents.slice(0, 3).join(", ");
402
- return `${name} ${provider} ${tier} ${inCost} ${outCost} ${gen} ${agents}`;
403
- });
404
- return [
405
- `${models.length} models${filter?.current === false ? " (including legacy)" : ""}`,
406
- "",
407
- header,
408
- divider,
409
- ...rows,
410
- ].join("\n");
411
- }
Binary file
@@ -1,85 +0,0 @@
1
- ---
2
- name: loadout-router
3
- description: Decide which model to use for a coding task and hand work to another agent. Use when the user asks which model to use, mentions running low on usage or cost, asks whether to switch to Opus or Sonnet or a GPT tier, or wants to delegate a task to Codex or Claude Code.
4
- ---
5
-
6
- # Loadout Router
7
-
8
- Pick the model that fits the work, using the user's own routing policy rather
9
- than your guess or mine.
10
-
11
- ## The policy is the user's, not yours
12
-
13
- ```bash
14
- loadout route
15
- ```
16
-
17
- That prints three buckets and the model the user has chosen for each:
18
-
19
- - **hard** — architecture, security, migrations, tricky debugging, risky review
20
- - **normal** — most implementation, ordinary debugging, refactors
21
- - **cheap** — tests, docs, boilerplate, renames, mechanical edits
22
-
23
- Read the policy before advising. If the user disagrees with a recommendation,
24
- the fix is to change the policy, not to argue:
25
-
26
- ```bash
27
- loadout route --set cheap=claude-sonnet-5
28
- ```
29
-
30
- ## Your job is the bucket, not the model
31
-
32
- The CLI can guess a bucket from wording, and it says so when it does. **You
33
- should do better**, because you have the conversation, the code, and the stakes.
34
- Decide the bucket yourself and state it:
35
-
36
- ```bash
37
- loadout route --bucket hard
38
- ```
39
-
40
- Judge by consequence, not vocabulary:
41
-
42
- - Anything touching auth, payments, migrations, or data deletion is **hard**,
43
- however small the diff.
44
- - Unfamiliar code is harder than familiar code doing the same thing.
45
- - A one-line change in a hot path is not cheap.
46
- - Genuinely mechanical work — a rename, a docstring, a test for code you just
47
- wrote — is **cheap**, and paying frontier prices for it is waste.
48
-
49
- Report the model and why that bucket. One or two sentences.
50
-
51
- ## When cost matters
52
-
53
- If the user mentions running low, being rate limited, or wanting to spend less,
54
- say what a cheaper bucket would cost them in quality rather than presenting it
55
- as free. `loadout route` shows real per-million prices for the comparison.
56
-
57
- Neither Claude Code nor Codex exposes remaining quota programmatically, so never
58
- claim to know how much the user has left.
59
-
60
- ## Handing work to the other agent
61
-
62
- One command sends a task; it sets up the shared log on first use:
63
-
64
- ```bash
65
- loadout handoff codex "write vitest coverage for src/auth.ts" --context "zod schemas already exist"
66
- ```
67
-
68
- Put everything the receiver needs into `--context` — file paths, decisions you
69
- already made, what you deliberately left out. It has none of this conversation.
70
-
71
- Sending writes to a shared file in the user's repository, so confirm first
72
- unless they asked for the handoff themselves.
73
-
74
- ## Reading your own inbox
75
-
76
- At session start, and after finishing a task:
77
-
78
- ```bash
79
- loadout handoff claude-code
80
- ```
81
-
82
- Work anything listed in order, then run the `loadout handoff --done <id>`
83
- command it prints. If nothing is pending, say nothing and carry on.
84
-
85
- `loadout handoff` with no arguments shows every pending task, both directions.