loadout-ai 0.6.0 → 0.8.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 +72 -0
- package/README.md +129 -26
- package/catalog/discovered.json +28469 -24524
- package/dist/src/cli.js +7 -2
- package/dist/src/commands/agents.js +6 -23
- package/dist/src/commands/catalog.js +186 -102
- package/dist/src/commands/health.js +6 -6
- package/dist/src/commands/inventory.js +19 -120
- package/dist/src/commands/lifecycle.js +13 -51
- package/dist/src/commands/mcp.js +10 -73
- package/dist/src/commands/setup.js +5 -5
- package/dist/src/commands/sharing.js +11 -108
- package/dist/src/commands/support.js +20 -9
- package/dist/src/core/{adapters.js → agents/adapters.js} +2 -2
- package/dist/src/core/{agent-inspection.js → agents/agent-inspection.js} +48 -6
- package/dist/src/core/{codex-mcp.js → agents/codex-mcp.js} +1 -1
- package/dist/src/core/{health-score-evidence.js → agents/health-score-evidence.js} +8 -8
- package/dist/src/core/{runtime-tools.js → agents/runtime-tools.js} +2 -2
- package/dist/src/core/{audit.js → catalog/audit.js} +4 -4
- package/dist/src/core/{catalog.js → catalog/catalog.js} +5 -5
- package/dist/src/core/{components.js → catalog/components.js} +2 -2
- package/dist/src/core/{conformance.js → catalog/conformance.js} +2 -2
- package/dist/src/core/{profiles.js → catalog/profiles.js} +2 -2
- package/dist/src/core/{provenance.js → catalog/provenance.js} +3 -3
- package/dist/src/core/{registry.js → catalog/registry.js} +59 -11
- package/dist/src/core/{safety.js → catalog/safety.js} +36 -7
- package/dist/src/core/{skill-compare.js → catalog/skill-compare.js} +1 -1
- package/dist/src/core/{skill-inventory.js → catalog/skill-inventory.js} +3 -3
- package/dist/src/core/{skills.js → catalog/skills.js} +1 -1
- package/dist/src/core/{candidate-intelligence.js → discovery/candidate-intelligence.js} +6 -6
- package/dist/src/core/{discovery-connector.js → discovery/discovery-connector.js} +2 -2
- package/dist/src/core/{evaluate.js → discovery/evaluate.js} +2 -2
- package/dist/src/core/{mcp-registry-discovery.js → discovery/mcp-registry-discovery.js} +1 -1
- package/dist/src/core/{observations.js → discovery/observations.js} +3 -3
- package/dist/src/core/{review-queue.js → discovery/review-queue.js} +2 -2
- package/dist/src/core/{skills-sh-discovery.js → discovery/skills-sh-discovery.js} +1 -1
- package/dist/src/core/{adopt.js → install/adopt.js} +3 -3
- package/dist/src/core/{catalog-install.js → install/catalog-install.js} +11 -11
- package/dist/src/core/{file-lock.js → install/file-lock.js} +1 -1
- package/dist/src/core/{install.js → install/install.js} +5 -5
- package/dist/src/core/{package.js → install/package.js} +3 -3
- package/dist/src/core/{reconcile.js → install/reconcile.js} +5 -5
- package/dist/src/core/{remove.js → install/remove.js} +4 -4
- package/dist/src/core/{snapshot.js → install/snapshot.js} +1 -1
- package/dist/src/core/{source.js → install/source.js} +22 -8
- package/dist/src/core/{sync.js → install/sync.js} +9 -9
- package/dist/src/core/{transaction.js → install/transaction.js} +1 -1
- package/dist/src/core/{uninstall.js → install/uninstall.js} +4 -4
- package/dist/src/core/{update.js → install/update.js} +4 -4
- package/dist/src/core/{cli-guide.js → reporting/cli-guide.js} +29 -45
- package/dist/src/core/reporting/completion.js +90 -0
- package/dist/src/core/{doctor.js → reporting/doctor.js} +5 -10
- package/dist/src/core/{freshness-alerts.js → reporting/freshness-alerts.js} +5 -5
- package/dist/src/core/{health.js → reporting/health.js} +6 -6
- package/dist/src/core/{loadout-card.js → reporting/loadout-card.js} +1 -1
- package/dist/src/core/{readme-claims.js → reporting/readme-claims.js} +1 -1
- package/dist/src/core/{readme-facts.js → reporting/readme-facts.js} +2 -2
- package/dist/src/core/{share-report.js → reporting/share-report.js} +3 -3
- package/dist/src/core/{upgrade.js → reporting/upgrade.js} +7 -7
- package/dist/src/core/routing/first-party-skills.js +118 -0
- package/dist/src/core/routing/handoff.js +314 -0
- package/dist/src/core/{model-config.js → routing/model-config.js} +4 -4
- package/dist/src/core/routing/policy.js +147 -0
- package/dist/src/core/{route.js → routing/route.js} +202 -39
- package/dist/src/core/{api.js → runtime/api.js} +4 -4
- package/dist/src/core/{canary.js → runtime/canary.js} +1 -1
- package/dist/src/core/{github.js → runtime/github.js} +1 -1
- package/dist/src/core/{mcp-recipes.js → runtime/mcp-recipes.js} +1 -1
- package/dist/src/core/{mcp.js → runtime/mcp.js} +2 -2
- package/dist/src/core/{scheduler.js → runtime/scheduler.js} +4 -4
- package/dist/src/core/{active-policy.js → workspace/active-policy.js} +2 -2
- package/dist/src/core/{active-set.js → workspace/active-set.js} +2 -2
- package/dist/src/core/{improve.js → workspace/improve.js} +3 -3
- package/dist/src/core/{manifest.js → workspace/manifest.js} +2 -2
- package/dist/src/core/{outcomes.js → workspace/outcomes.js} +3 -3
- package/dist/src/core/{portable.js → workspace/portable.js} +3 -3
- package/dist/src/core/{profile-state.js → workspace/profile-state.js} +1 -1
- package/dist/src/core/{recommend.js → workspace/recommend.js} +1 -1
- package/dist/src/core/{state.js → workspace/state.js} +3 -3
- package/docs/CANDIDATE_INTELLIGENCE.md +9 -2
- package/docs/CATALOG.md +1 -1
- package/docs/CREDENTIAL_AND_UPDATE_POLICY.md +1 -1
- package/docs/DISCOVERED.md +250 -248
- package/docs/FEATURE_TEST_MATRIX.md +7 -260
- package/docs/GITHUB_AUTHORIZATION.md +5 -0
- package/docs/PROVENANCE_AND_COMPARISON.md +1 -1
- package/docs/RELEASE_REVIEW.md +0 -1
- package/docs/evidence/readme-claims.json +22 -19
- package/package.json +7 -4
- package/skills/loadout-curator/SKILL.md +105 -0
- package/skills/loadout-router/SKILL.md +85 -0
- package/MASTER_PLAN.md +0 -2207
- package/dist/src/core/completion.js +0 -143
- package/dist/src/core/handoff.js +0 -161
- 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/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/dist/src/core/{agent-health-score.js → agents/agent-health-score.js} +0 -0
- /package/dist/src/core/{agent-versions.js → agents/agent-versions.js} +0 -0
- /package/dist/src/core/{conversion.js → agents/conversion.js} +0 -0
- /package/dist/src/core/{paths.js → agents/paths.js} +0 -0
- /package/dist/src/core/{runtime-tool-recipe.js → agents/runtime-tool-recipe.js} +0 -0
- /package/dist/src/core/{catalog-coverage.js → catalog/catalog-coverage.js} +0 -0
- /package/dist/src/core/{skill-security.js → catalog/skill-security.js} +0 -0
- /package/dist/src/core/{community.js → discovery/community.js} +0 -0
- /package/dist/src/core/{github-discovery.js → discovery/github-discovery.js} +0 -0
- /package/dist/src/core/{private-discovery.js → discovery/private-discovery.js} +0 -0
- /package/dist/src/core/{ranking.js → discovery/ranking.js} +0 -0
- /package/dist/src/core/{atomic-file.js → install/atomic-file.js} +0 -0
- /package/dist/src/core/{diff.js → install/diff.js} +0 -0
- /package/dist/src/core/{update-watch.js → install/update-watch.js} +0 -0
- /package/dist/src/core/{loadout-badge.js → reporting/loadout-badge.js} +0 -0
- /package/dist/src/core/{terminal.js → reporting/terminal.js} +0 -0
- /package/dist/src/core/{access.js → routing/access.js} +0 -0
- /package/dist/src/core/{credentials.js → routing/credentials.js} +0 -0
- /package/dist/src/core/{sandbox.js → runtime/sandbox.js} +0 -0
- /package/dist/src/core/{active-limit.js → workspace/active-limit.js} +0 -0
- /package/dist/src/core/{target-occupancy.js → workspace/target-occupancy.js} +0 -0
package/dist/src/cli.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command, CommanderError } from "commander";
|
|
3
|
-
import { HIDDEN_FROM_FIRST_SCREEN } from "./core/cli-guide.js";
|
|
4
|
-
import {
|
|
3
|
+
import { HIDDEN_FROM_FIRST_SCREEN } from "./core/reporting/cli-guide.js";
|
|
4
|
+
import { registerCompletionCommands } from "./core/reporting/completion.js";
|
|
5
|
+
import { recoverPendingTransactions } from "./core/install/transaction.js";
|
|
5
6
|
import { LOADOUT_VERSION, printBeginnerGuide, runWizard, } from "./commands/support.js";
|
|
6
7
|
import { registerSetup } from "./commands/setup.js";
|
|
7
8
|
import { registerSharing } from "./commands/sharing.js";
|
|
@@ -34,6 +35,10 @@ registerInventory(program);
|
|
|
34
35
|
registerCatalog(program);
|
|
35
36
|
registerMcp(program);
|
|
36
37
|
registerLifecycle(program);
|
|
38
|
+
registerCompletionCommands(program.commands.map((command) => ({
|
|
39
|
+
name: command.name(),
|
|
40
|
+
subcommands: command.commands.map((sub) => sub.name()),
|
|
41
|
+
})));
|
|
37
42
|
for (const command of program.commands)
|
|
38
43
|
// Commander supports `hidden` when a command is created. These commands are
|
|
39
44
|
// registered by separate feature blocks, so use the same runtime flag here
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { detectAgents, parseAgentSelection } from "../core/paths.js";
|
|
2
|
-
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "../core/active-limit.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { applyNativeSchedulerBundle, formatNativeScheduler, planNativeScheduler, } from "../core/scheduler.js";
|
|
8
|
-
import { REVIEWED_RUNTIME_TOOLS, applyRuntimeToolPlan, formatRuntimeToolPlan, planRuntimeTool, } from "../core/runtime-tools.js";
|
|
9
|
-
import { buildLoadoutCard } from "../core/loadout-card.js";
|
|
10
|
-
import { buildLoadoutBadge, formatLoadoutBadgeUsage, parseLoadoutBadgeMetric, } from "../core/loadout-badge.js";
|
|
1
|
+
import { detectAgents, parseAgentSelection } from "../core/agents/paths.js";
|
|
2
|
+
import { DEFAULT_ACTIVE_SKILL_LIMIT } from "../core/workspace/active-limit.js";
|
|
3
|
+
import { applyActivationChange, formatActivationPlan, planActivationChange, } from "../core/workspace/active-set.js";
|
|
4
|
+
import { applyProjectActivation, formatProjectActivation, planProjectActivation, } from "../core/workspace/active-policy.js";
|
|
5
|
+
import { applyNativeSchedulerBundle, formatNativeScheduler, planNativeScheduler, } from "../core/runtime/scheduler.js";
|
|
6
|
+
import { REVIEWED_RUNTIME_TOOLS, applyRuntimeToolPlan, formatRuntimeToolPlan, planRuntimeTool, } from "../core/agents/runtime-tools.js";
|
|
11
7
|
import { collectOption, durableSchedulerLauncher } from "./support.js";
|
|
12
8
|
export function registerAgents(program) {
|
|
13
9
|
for (const workflow of ["activate", "optimize"]) {
|
|
@@ -108,19 +104,6 @@ export function registerAgents(program) {
|
|
|
108
104
|
? JSON.stringify({ action, plans, snapshotId }, null, 2)
|
|
109
105
|
: `Loadout Autopilot ${options.remove ? "removed" : "enabled"}: daily update radar + multi-source candidate discovery.\nNo scheduled command can install, promote, or execute a candidate. Snapshot: ${snapshotId}`);
|
|
110
106
|
});
|
|
111
|
-
program
|
|
112
|
-
.command("badge")
|
|
113
|
-
.description("Generate telemetry-free Shields endpoint JSON from the local privacy-safe card")
|
|
114
|
-
.option("--metric <metric>", "evidence, active-skills, managed-packages, or mcp", "evidence")
|
|
115
|
-
.option("--output <path>", "write endpoint JSON to this path")
|
|
116
|
-
.action(async (options) => {
|
|
117
|
-
const badge = buildLoadoutBadge(await buildLoadoutCard(), parseLoadoutBadgeMetric(options.metric));
|
|
118
|
-
if (!options.output)
|
|
119
|
-
return console.log(JSON.stringify(badge, null, 2));
|
|
120
|
-
const output = resolve(options.output);
|
|
121
|
-
await writeFileAtomically(output, `${JSON.stringify(badge, null, 2)}\n`);
|
|
122
|
-
console.log(formatLoadoutBadgeUsage(output));
|
|
123
|
-
});
|
|
124
107
|
program
|
|
125
108
|
.command("tool")
|
|
126
109
|
.description("Preview, install, or remove an explicitly reviewed runtime-tool recipe")
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
import { explainCatalogScore, loadEffectiveCatalog, loadCatalog, promoteCatalogCandidate, rankCatalog, refreshCatalog, validateCatalog, } from "../core/catalog.js";
|
|
2
|
-
import { parseAgentSelection } from "../core/paths.js";
|
|
1
|
+
import { explainCatalogScore, loadEffectiveCatalog, loadCatalog, promoteCatalogCandidate, rankCatalog, refreshCatalog, validateCatalog, } from "../core/catalog/catalog.js";
|
|
2
|
+
import { detectAgents, parseAgentSelection } from "../core/agents/paths.js";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
5
|
+
import { formatModelCatalog, MODEL_CATALOG } from "../core/routing/route.js";
|
|
6
|
+
import { BUCKETS, formatAnswer, formatPolicy, guessBucket, policyPath, readPolicy, resolveRoute, setRule, writePolicy, } from "../core/routing/policy.js";
|
|
7
|
+
import { rm } from "node:fs/promises";
|
|
8
|
+
import { applyFirstPartySkill, FIRST_PARTY_SKILLS, formatFirstPartySkillList, formatFirstPartySkillPlan, installedFirstPartySkills, planFirstPartySkill, removeFirstPartySkill, } from "../core/routing/first-party-skills.js";
|
|
9
|
+
import { applyPickup, formatHandoffStatus, formatInbox, getHandoffState, initHandoff, isHandoffInitialized, isPickupTarget, markDone, planPickup, readInbox, sendHandoff, } from "../core/routing/handoff.js";
|
|
10
|
+
import { catalogTrustStage, formatCatalogTrustStage, } from "../core/catalog/profiles.js";
|
|
11
|
+
import { discoverHackerNewsRepositories } from "../core/discovery/community.js";
|
|
12
|
+
import { discoverPrivateRepositories } from "../core/discovery/private-discovery.js";
|
|
13
|
+
import { defaultGitHubDiscoveryQueries, discoverGitHubRepositories, } from "../core/discovery/github-discovery.js";
|
|
14
|
+
import { formatStarHistory, readCatalogObservations, } from "../core/discovery/observations.js";
|
|
15
|
+
import { writeFileAtomically } from "../core/install/atomic-file.js";
|
|
16
|
+
import { formatReviewQueue, markPromoted, mergeReviewQueue, readReviewQueue, } from "../core/discovery/review-queue.js";
|
|
17
|
+
import { applyProviderModelSelection, defaultModelConfigurationPath, formatProviderModelConfiguration, planProviderModelSelection, readProviderModelConfiguration, requestOpenRouter, } from "../core/routing/model-config.js";
|
|
18
|
+
import { parseCompletionShell, renderShellCompletion, } from "../core/reporting/completion.js";
|
|
19
|
+
import { buildCatalogCoverage, formatCatalogCoverage, } from "../core/catalog/catalog-coverage.js";
|
|
20
|
+
import { createCredentialResolver, createOsCredentialStore, } from "../core/routing/credentials.js";
|
|
21
|
+
import { buildCandidateDossier, buildCatalogProposal, formatCandidateDossier, formatCandidateSummaries, listDiscoveryCandidates, readCandidateDossier, verifyCandidateDossierSource, writeCandidateDossier, } from "../core/discovery/candidate-intelligence.js";
|
|
22
|
+
import { discoverSkillsSh } from "../core/discovery/skills-sh-discovery.js";
|
|
23
|
+
import { discoverOfficialMcpRegistry } from "../core/discovery/mcp-registry-discovery.js";
|
|
21
24
|
import { readCredentialFromStdin } from "./support.js";
|
|
22
25
|
export function registerCatalog(program) {
|
|
23
26
|
program
|
|
@@ -444,17 +447,6 @@ export function registerCatalog(program) {
|
|
|
444
447
|
? JSON.stringify(filtered, null, 2)
|
|
445
448
|
: formatReviewQueue(filtered));
|
|
446
449
|
});
|
|
447
|
-
program
|
|
448
|
-
.command("review")
|
|
449
|
-
.description("Record a human queue decision; shortlisting still does not promote or install")
|
|
450
|
-
.argument("<repository>", "owner/repository")
|
|
451
|
-
.requiredOption("--decision <value>", "pending, shortlisted, or ignored")
|
|
452
|
-
.action(async (repository, options) => {
|
|
453
|
-
if (!["pending", "shortlisted", "ignored"].includes(options.decision))
|
|
454
|
-
throw new Error("--decision must be pending, shortlisted, or ignored");
|
|
455
|
-
const item = await setReviewDecision(repository, options.decision);
|
|
456
|
-
console.log(`${item.repository}: ${item.decision}. No catalog or agent files changed.`);
|
|
457
|
-
});
|
|
458
450
|
const credentials = program
|
|
459
451
|
.command("credentials")
|
|
460
452
|
.description("Store, inspect, or remove secrets in the native OS credential store");
|
|
@@ -600,103 +592,195 @@ export function registerCatalog(program) {
|
|
|
600
592
|
});
|
|
601
593
|
console.log(`Verified model selection '${id}'. No credential value was stored or printed.`);
|
|
602
594
|
});
|
|
603
|
-
const
|
|
604
|
-
.command("
|
|
605
|
-
.description("
|
|
606
|
-
|
|
607
|
-
.command("
|
|
608
|
-
.description("
|
|
609
|
-
.action(async () => {
|
|
610
|
-
const projectRoot = process.cwd();
|
|
611
|
-
if (await isHandoffInitialized(projectRoot)) {
|
|
612
|
-
console.log("Handoff already initialized at .handoff/");
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
const dir = await initHandoff(projectRoot);
|
|
616
|
-
console.log(`Initialized handoff protocol at ${dir}`);
|
|
617
|
-
console.log("Add .handoff/ to version control so both agents share the message log.");
|
|
618
|
-
});
|
|
619
|
-
handoff
|
|
620
|
-
.command("send")
|
|
621
|
-
.description("Send a task or message to another agent")
|
|
622
|
-
.argument("<agent>", "target agent (e.g. codex, claude-code)")
|
|
623
|
-
.argument("<description...>", "task description")
|
|
624
|
-
.option("--from <agent>", "sending agent", "user")
|
|
625
|
-
.option("--type <type>", "message type: task, handoff, question, status, error, cancel", "task")
|
|
626
|
-
.option("--context <text>", "additional context for the receiving agent")
|
|
595
|
+
const skills = program
|
|
596
|
+
.command("skills")
|
|
597
|
+
.description("Install the skills Loadout ships so your agents can use Loadout from inside a conversation");
|
|
598
|
+
skills
|
|
599
|
+
.command("list", { isDefault: true })
|
|
600
|
+
.description("Show the skills Loadout ships and whether they are installed")
|
|
627
601
|
.option("--json", "emit machine-readable JSON")
|
|
628
|
-
.action(async (
|
|
629
|
-
const
|
|
630
|
-
from: options.from,
|
|
631
|
-
type: options.type,
|
|
632
|
-
context: options.context,
|
|
633
|
-
});
|
|
602
|
+
.action(async (options) => {
|
|
603
|
+
const installed = await installedFirstPartySkills();
|
|
634
604
|
console.log(options.json
|
|
635
|
-
? JSON.stringify(
|
|
636
|
-
|
|
605
|
+
? JSON.stringify(FIRST_PARTY_SKILLS.map((skill) => ({
|
|
606
|
+
...skill,
|
|
607
|
+
installed: installed.has(skill.id),
|
|
608
|
+
})), null, 2)
|
|
609
|
+
: formatFirstPartySkillList(installed));
|
|
637
610
|
});
|
|
638
|
-
|
|
639
|
-
.command("
|
|
640
|
-
.description("
|
|
641
|
-
.argument("<id>", "
|
|
642
|
-
.option("--
|
|
611
|
+
skills
|
|
612
|
+
.command("install")
|
|
613
|
+
.description("Preview or install one Loadout skill into detected agents")
|
|
614
|
+
.argument("<id>", "skill id, for example loadout-router")
|
|
615
|
+
.option("--agents <ids>", "comma-separated agent ids; defaults to all detected")
|
|
616
|
+
.option("--yes", "apply after previewing")
|
|
643
617
|
.action(async (id, options) => {
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
618
|
+
const plan = await planFirstPartySkill(id, {
|
|
619
|
+
...(options.agents
|
|
620
|
+
? { agents: parseAgentSelection(options.agents) }
|
|
621
|
+
: {}),
|
|
622
|
+
});
|
|
623
|
+
console.log(formatFirstPartySkillPlan(plan));
|
|
624
|
+
if (!plan.targets.length)
|
|
625
|
+
return;
|
|
626
|
+
if (!options.yes) {
|
|
627
|
+
console.log("\nPreview only. Re-run with --yes to install.");
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
await applyFirstPartySkill(plan);
|
|
631
|
+
console.log(`\nInstalled '${plan.skill.id}' for ${plan.targets.length} agent(s). Start a new agent session to pick it up.`);
|
|
648
632
|
});
|
|
633
|
+
skills
|
|
634
|
+
.command("remove")
|
|
635
|
+
.description("Preview or remove one Loadout skill from detected agents")
|
|
636
|
+
.argument("<id>", "skill id")
|
|
637
|
+
.option("--agents <ids>", "comma-separated agent ids; defaults to all detected")
|
|
638
|
+
.option("--yes", "apply after previewing")
|
|
639
|
+
.action(async (id, options) => {
|
|
640
|
+
const plan = await planFirstPartySkill(id, {
|
|
641
|
+
...(options.agents
|
|
642
|
+
? { agents: parseAgentSelection(options.agents) }
|
|
643
|
+
: {}),
|
|
644
|
+
});
|
|
645
|
+
const present = plan.targets.filter((target) => target.replacing);
|
|
646
|
+
if (!present.length) {
|
|
647
|
+
console.log(`'${id}' is not installed for any detected agent.`);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
for (const target of present)
|
|
651
|
+
console.log(` remove → ${target.destination} [${target.displayName}]`);
|
|
652
|
+
if (!options.yes) {
|
|
653
|
+
console.log("\nPreview only. Re-run with --yes to remove.");
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
await removeFirstPartySkill(plan);
|
|
657
|
+
console.log(`\nRemoved '${id}' from ${present.length} agent(s).`);
|
|
658
|
+
});
|
|
659
|
+
const handoff = program
|
|
660
|
+
.command("handoff")
|
|
661
|
+
.description("Hand a task to your other agent, or see what has been handed to you");
|
|
649
662
|
handoff
|
|
650
|
-
.
|
|
651
|
-
.
|
|
663
|
+
.argument("[agent]", "who should do it, for example codex")
|
|
664
|
+
.argument("[task...]", "what they should do")
|
|
665
|
+
.option("--context <text>", "anything they need that is not in the task")
|
|
666
|
+
.option("--from <agent>", "who is sending", "user")
|
|
667
|
+
.option("--done <id>", "mark a task finished")
|
|
652
668
|
.option("--json", "emit machine-readable JSON")
|
|
653
|
-
.action(async (options) => {
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
669
|
+
.action(async (agent, taskWords, options) => {
|
|
670
|
+
const cwd = process.cwd();
|
|
671
|
+
if (options.done) {
|
|
672
|
+
const message = await markDone(cwd, options.done);
|
|
673
|
+
console.log(options.json
|
|
674
|
+
? JSON.stringify(message, null, 2)
|
|
675
|
+
: `Marked ${options.done} done.`);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
// No agent named: show what is waiting, for everyone.
|
|
679
|
+
if (!agent) {
|
|
680
|
+
const state = await getHandoffState(cwd);
|
|
681
|
+
console.log(options.json
|
|
682
|
+
? JSON.stringify(state, null, 2)
|
|
683
|
+
: formatHandoffStatus(state));
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
// An agent with no task means "show me my inbox".
|
|
687
|
+
const task = taskWords.join(" ").trim();
|
|
688
|
+
if (!task) {
|
|
689
|
+
const messages = await readInbox(cwd, agent);
|
|
690
|
+
console.log(options.json
|
|
691
|
+
? JSON.stringify(messages, null, 2)
|
|
692
|
+
: formatInbox(agent, messages));
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
// Sending is the common case, so it sets itself up rather than failing
|
|
696
|
+
// with instructions to run two other commands first.
|
|
697
|
+
const setup = [];
|
|
698
|
+
if (!(await isHandoffInitialized(cwd))) {
|
|
699
|
+
await initHandoff(cwd);
|
|
700
|
+
setup.push("created .handoff/");
|
|
701
|
+
}
|
|
702
|
+
for (const target of [options.from, agent]) {
|
|
703
|
+
if (!isPickupTarget(target))
|
|
704
|
+
continue;
|
|
705
|
+
const plan = await planPickup(cwd, target);
|
|
706
|
+
if (!plan.replacing) {
|
|
707
|
+
await applyPickup(plan);
|
|
708
|
+
setup.push(`told ${target} to check its inbox`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
const message = await sendHandoff(cwd, agent, task, {
|
|
712
|
+
from: options.from,
|
|
713
|
+
...(options.context ? { context: options.context } : {}),
|
|
714
|
+
});
|
|
715
|
+
if (options.json) {
|
|
716
|
+
console.log(JSON.stringify({ message, setup }, null, 2));
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
for (const line of setup)
|
|
720
|
+
console.log(` ${line}`);
|
|
721
|
+
console.log(`Sent to ${agent}: ${task}`);
|
|
722
|
+
console.log(`It will pick this up next session, or now with: loadout handoff ${agent}`);
|
|
658
723
|
});
|
|
659
724
|
const route = program
|
|
660
725
|
.command("route")
|
|
661
|
-
.description("
|
|
726
|
+
.description("Which model to use for a task, according to a routing policy you own");
|
|
662
727
|
route
|
|
663
|
-
.argument("[description...]", "
|
|
664
|
-
.option("--
|
|
665
|
-
.option("--
|
|
666
|
-
.option("--
|
|
667
|
-
.option("--
|
|
668
|
-
.option("--
|
|
669
|
-
.option("--tier <tier>", "filter models by tier (frontier, standard, fast)")
|
|
728
|
+
.argument("[description...]", "what you are about to do")
|
|
729
|
+
.option("--bucket <bucket>", "state the bucket yourself: hard, normal, or cheap")
|
|
730
|
+
.option("--set <bucket=model>", "change the model for one bucket and save")
|
|
731
|
+
.option("--save", "write the current defaults to disk so you can edit them")
|
|
732
|
+
.option("--reset", "delete your policy and go back to the defaults")
|
|
733
|
+
.option("--models", "list the model ids you can name in a policy")
|
|
670
734
|
.option("--json", "emit machine-readable JSON")
|
|
671
735
|
.action(async (descriptionWords, options) => {
|
|
672
736
|
if (options.models) {
|
|
673
737
|
console.log(options.json
|
|
674
|
-
? JSON.stringify(
|
|
675
|
-
: formatModelCatalog(
|
|
676
|
-
provider: options.provider,
|
|
677
|
-
tier: options.tier,
|
|
678
|
-
}));
|
|
738
|
+
? JSON.stringify(MODEL_CATALOG, null, 2)
|
|
739
|
+
: formatModelCatalog());
|
|
679
740
|
return;
|
|
680
741
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
742
|
+
const detected = await detectAgents();
|
|
743
|
+
const installed = detected
|
|
744
|
+
.filter((agent) => agent.installed)
|
|
745
|
+
.map((agent) => agent.id);
|
|
746
|
+
if (options.reset) {
|
|
747
|
+
await rm(policyPath(), { force: true });
|
|
748
|
+
console.log("Removed your routing policy; defaults apply again.");
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (options.set) {
|
|
752
|
+
const separator = options.set.indexOf("=");
|
|
753
|
+
if (separator <= 0)
|
|
754
|
+
throw new Error("--set expects <bucket>=<model>, for example normal=gpt-5.6-terra");
|
|
755
|
+
const bucket = options.set.slice(0, separator).trim();
|
|
756
|
+
if (!BUCKETS.includes(bucket))
|
|
757
|
+
throw new Error(`Unknown bucket '${bucket}'. Valid: ${BUCKETS.join(", ")}`);
|
|
758
|
+
const updated = await setRule(bucket, options.set.slice(separator + 1).trim(), installed);
|
|
759
|
+
console.log(formatPolicy(updated, "file"));
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const { policy, source } = await readPolicy(installed);
|
|
763
|
+
if (options.save) {
|
|
764
|
+
const path = await writePolicy(policy);
|
|
765
|
+
console.log(`Saved your routing policy to ${path}\n`);
|
|
766
|
+
console.log(formatPolicy(policy, "file"));
|
|
685
767
|
return;
|
|
686
768
|
}
|
|
687
769
|
const description = descriptionWords.join(" ").trim();
|
|
688
|
-
if (!description && !options.
|
|
770
|
+
if (!description && !options.bucket) {
|
|
689
771
|
console.log(options.json
|
|
690
|
-
? JSON.stringify(
|
|
691
|
-
:
|
|
772
|
+
? JSON.stringify({ policy, source }, null, 2)
|
|
773
|
+
: formatPolicy(policy, source));
|
|
692
774
|
return;
|
|
693
775
|
}
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
:
|
|
776
|
+
const stated = options.bucket;
|
|
777
|
+
if (stated && !BUCKETS.includes(stated))
|
|
778
|
+
throw new Error(`Unknown bucket '${stated}'. Valid: ${BUCKETS.join(", ")}`);
|
|
779
|
+
const bucket = stated ?? guessBucket(description);
|
|
780
|
+
const answer = resolveRoute(policy, bucket, installed, !stated);
|
|
697
781
|
console.log(options.json
|
|
698
|
-
? JSON.stringify(
|
|
699
|
-
:
|
|
782
|
+
? JSON.stringify(answer, null, 2)
|
|
783
|
+
: formatAnswer(answer, description || undefined));
|
|
700
784
|
});
|
|
701
785
|
program
|
|
702
786
|
.command("completion")
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { parseAgentSelection } from "../core/paths.js";
|
|
2
|
-
import { buildUpdatePlan } from "../core/update.js";
|
|
3
|
-
import { buildHealthReport, formatHealthReport } from "../core/health.js";
|
|
4
|
-
import { buildFreshnessAlerts, formatFreshnessAlerts, ignoreFreshnessAlert, pinReplacement, readReplacementPins, unpinReplacement, } from "../core/freshness-alerts.js";
|
|
5
|
-
import { formatAgentHealthScore } from "../core/agent-health-score.js";
|
|
6
|
-
import { buildLocalAgentHealthScores } from "../core/health-score-evidence.js";
|
|
1
|
+
import { parseAgentSelection } from "../core/agents/paths.js";
|
|
2
|
+
import { buildUpdatePlan } from "../core/install/update.js";
|
|
3
|
+
import { buildHealthReport, formatHealthReport, } from "../core/reporting/health.js";
|
|
4
|
+
import { buildFreshnessAlerts, formatFreshnessAlerts, ignoreFreshnessAlert, pinReplacement, readReplacementPins, unpinReplacement, } from "../core/reporting/freshness-alerts.js";
|
|
5
|
+
import { formatAgentHealthScore } from "../core/agents/agent-health-score.js";
|
|
6
|
+
import { buildLocalAgentHealthScores } from "../core/agents/health-score-evidence.js";
|
|
7
7
|
export function registerHealth(program) {
|
|
8
8
|
program
|
|
9
9
|
.command("health")
|
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
import { loadEffectiveCatalog } from "../core/catalog.js";
|
|
2
|
-
import { detectAgents, parseAgentSelection } from "../core/paths.js";
|
|
1
|
+
import { loadEffectiveCatalog } from "../core/catalog/catalog.js";
|
|
2
|
+
import { detectAgents, parseAgentSelection } from "../core/agents/paths.js";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import { runDoctor, formatDoctorReport } from "../core/doctor.js";
|
|
5
|
-
import { applyProfileToManifest } from "../core/manifest.js";
|
|
6
|
-
import { buildHealthReport, formatStatusScreen, gradeHealth, } from "../core/health.js";
|
|
7
|
-
import { applyRemove, planRemove } from "../core/remove.js";
|
|
8
|
-
import { applyUninstall, buildUninstallPlan, formatUninstallPlan, uninstallGlobalCli, } from "../core/uninstall.js";
|
|
9
|
-
import { formatRecommendations, personalizeRecommendations, profileManifestPackages, recommendPackages, RECOMMENDATION_BOUNDARY, scanProject, TESTED_PROFILES, } from "../core/recommend.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { readLocalOutcomes } from "../core/outcomes.js";
|
|
20
|
-
import { formatAgentVersions, inspectAgentVersions, } from "../core/agent-versions.js";
|
|
21
|
-
import { scanSkillSecurity } from "../core/skill-security.js";
|
|
4
|
+
import { runDoctor, formatDoctorReport } from "../core/reporting/doctor.js";
|
|
5
|
+
import { applyProfileToManifest } from "../core/workspace/manifest.js";
|
|
6
|
+
import { buildHealthReport, formatStatusScreen, gradeHealth, } from "../core/reporting/health.js";
|
|
7
|
+
import { applyRemove, planRemove } from "../core/install/remove.js";
|
|
8
|
+
import { applyUninstall, buildUninstallPlan, formatUninstallPlan, uninstallGlobalCli, } from "../core/install/uninstall.js";
|
|
9
|
+
import { formatRecommendations, personalizeRecommendations, profileManifestPackages, recommendPackages, RECOMMENDATION_BOUNDARY, scanProject, TESTED_PROFILES, } from "../core/workspace/recommend.js";
|
|
10
|
+
import { applySyncPlan, buildSyncPlan } from "../core/install/sync.js";
|
|
11
|
+
import { formatAgentInventory, inspectAgents, } from "../core/agents/agent-inspection.js";
|
|
12
|
+
import { formatInstalledSkillInventory, scanInstalledSkills, } from "../core/catalog/skill-inventory.js";
|
|
13
|
+
import { enrichInventoryWithProvenance, formatProvenanceSummary, resolveCatalogSkillIndex, } from "../core/catalog/provenance.js";
|
|
14
|
+
import { applySkillAdoption, formatAdoptionPlan, planSkillAdoption, } from "../core/install/adopt.js";
|
|
15
|
+
import { applyReconcilePlan, buildReconcilePlan, formatReconcilePlan, } from "../core/install/reconcile.js";
|
|
16
|
+
import { readLocalOutcomes } from "../core/workspace/outcomes.js";
|
|
17
|
+
import { formatAgentVersions, inspectAgentVersions, } from "../core/agents/agent-versions.js";
|
|
18
|
+
import { scanSkillSecurity } from "../core/catalog/skill-security.js";
|
|
22
19
|
import { printProvenanceProgress } from "./support.js";
|
|
23
20
|
export function registerInventory(program) {
|
|
24
21
|
program
|
|
@@ -117,35 +114,6 @@ export function registerInventory(program) {
|
|
|
117
114
|
? JSON.stringify({ name, ...TESTED_PROFILES[name], packages }, null, 2)
|
|
118
115
|
: `${name}: ${TESTED_PROFILES[name].description}\n${packages.map((pkg) => ` ${pkg.id} — ${pkg.repository}`).join("\n")}`);
|
|
119
116
|
});
|
|
120
|
-
program
|
|
121
|
-
.command("improve")
|
|
122
|
-
.description("Propose the next evidence-backed improvement without changing anything")
|
|
123
|
-
.option("--json", "emit machine-readable JSON")
|
|
124
|
-
.option("--write", "persist the cycle record and reusable prompt locally")
|
|
125
|
-
.option("--output <directory>", "output directory; defaults to private Loadout state")
|
|
126
|
-
.action(async (options) => {
|
|
127
|
-
const cycle = await buildImprovementCycle();
|
|
128
|
-
console.log(options.json
|
|
129
|
-
? JSON.stringify(cycle, null, 2)
|
|
130
|
-
: formatImprovementCycle(cycle));
|
|
131
|
-
if (options.write) {
|
|
132
|
-
const paths = await writeImprovementCycle(cycle, options.output);
|
|
133
|
-
console.log(`Cycle record: ${paths.json}\nLoop prompt: ${paths.prompt}`);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
program
|
|
137
|
-
.command("improve-feedback")
|
|
138
|
-
.description("Record a human-reviewed outcome for a persisted improvement cycle")
|
|
139
|
-
.requiredOption("--id <id>", "cycle id")
|
|
140
|
-
.requiredOption("--outcome <outcome>", "success, failure, or partial")
|
|
141
|
-
.option("--note <text>", "short non-secret lesson")
|
|
142
|
-
.option("--directory <path>", "improvement history directory")
|
|
143
|
-
.action(async (options) => {
|
|
144
|
-
if (!["success", "failure", "partial"].includes(options.outcome))
|
|
145
|
-
throw new Error("--outcome must be success, failure, or partial");
|
|
146
|
-
await recordImprovementOutcome(options.id, options.outcome, options.note, options.directory);
|
|
147
|
-
console.log(`Recorded ${options.outcome} outcome for ${options.id}.`);
|
|
148
|
-
});
|
|
149
117
|
program
|
|
150
118
|
.command("sync")
|
|
151
119
|
.description("Reproduce a loadout.json manifest as one safe transaction")
|
|
@@ -284,53 +252,10 @@ export function registerInventory(program) {
|
|
|
284
252
|
? JSON.stringify({ plan, snapshotId }, null, 2)
|
|
285
253
|
: `${formatAdoptionPlan(plan)}\nAdopted without changing skill bytes. Snapshot: ${snapshotId}`);
|
|
286
254
|
});
|
|
287
|
-
program
|
|
288
|
-
.command("compare")
|
|
289
|
-
.description("Compare an installed or reviewed skill with evidence-related catalog alternatives without changing anything")
|
|
290
|
-
.argument("<skill>", "installed skill name, directory name, or catalog package id")
|
|
291
|
-
.option("--agent <id>", "select one installed agent when names are ambiguous")
|
|
292
|
-
.option("--refresh", "rebuild the inspected catalog skill index")
|
|
293
|
-
.option("--offline", "never fetch; use the existing local index only")
|
|
294
|
-
.option("--limit <count>", "maximum alternatives to show", "10")
|
|
295
|
-
.option("--json", "emit the complete machine-readable comparison")
|
|
296
|
-
.action(async (skill, options) => {
|
|
297
|
-
if (options.refresh && options.offline)
|
|
298
|
-
throw new Error("--refresh and --offline cannot be used together");
|
|
299
|
-
const limit = Number(options.limit);
|
|
300
|
-
if (!Number.isInteger(limit) || limit < 1 || limit > 50)
|
|
301
|
-
throw new Error("--limit must be an integer from 1 to 50");
|
|
302
|
-
const detected = await detectAgents();
|
|
303
|
-
const agent = options.agent;
|
|
304
|
-
if (agent && !detected.some((item) => item.id === agent))
|
|
305
|
-
throw new Error(`Unknown agent id: ${options.agent}`);
|
|
306
|
-
const selected = detected.filter((item) => item.installed);
|
|
307
|
-
if (!selected.length)
|
|
308
|
-
throw new Error("No detected agent profile is available to compare");
|
|
309
|
-
const catalog = await loadEffectiveCatalog();
|
|
310
|
-
const resolved = await resolveCatalogSkillIndex({
|
|
311
|
-
refresh: options.refresh,
|
|
312
|
-
offline: options.offline,
|
|
313
|
-
build: {
|
|
314
|
-
catalog,
|
|
315
|
-
onProgress: options.offline ? undefined : printProvenanceProgress,
|
|
316
|
-
},
|
|
317
|
-
});
|
|
318
|
-
if (!resolved.index)
|
|
319
|
-
throw new Error("No local catalog skill index exists. Re-run without --offline or use --refresh.");
|
|
320
|
-
const inventory = enrichInventoryWithProvenance(await scanInstalledSkills(selected), resolved.index, resolved.source);
|
|
321
|
-
const comparison = compareSkill(skill, inventory, resolved.index.records, catalog, {
|
|
322
|
-
...(agent ? { agent } : {}),
|
|
323
|
-
limit,
|
|
324
|
-
indexGeneratedAt: resolved.index.generatedAt,
|
|
325
|
-
failures: resolved.index.failures,
|
|
326
|
-
});
|
|
327
|
-
console.log(options.json
|
|
328
|
-
? JSON.stringify(comparison, null, 2)
|
|
329
|
-
: formatSkillComparison(comparison));
|
|
330
|
-
});
|
|
331
255
|
program
|
|
332
256
|
.command("status")
|
|
333
257
|
.description("Home screen: a health grade, detected agents, and the next thing to do")
|
|
258
|
+
.option("--details", "show per-component adapter compatibility for each agent")
|
|
334
259
|
.option("--json", "emit machine-readable status")
|
|
335
260
|
.action(async (options) => {
|
|
336
261
|
const agents = await detectAgents();
|
|
@@ -340,7 +265,7 @@ export function registerInventory(program) {
|
|
|
340
265
|
]);
|
|
341
266
|
if (options.json)
|
|
342
267
|
return console.log(JSON.stringify({ grade: gradeHealth(report), report, inventory }, null, 2));
|
|
343
|
-
console.log(formatStatusScreen(report, inventory.map((item) => formatAgentInventory(item))));
|
|
268
|
+
console.log(formatStatusScreen(report, inventory.map((item) => formatAgentInventory(item, { details: options.details }))));
|
|
344
269
|
});
|
|
345
270
|
program
|
|
346
271
|
.command("versions")
|
|
@@ -377,32 +302,6 @@ export function registerInventory(program) {
|
|
|
377
302
|
if (report.verdict === "blocked")
|
|
378
303
|
process.exitCode = 2;
|
|
379
304
|
});
|
|
380
|
-
program
|
|
381
|
-
.command("capabilities")
|
|
382
|
-
.description("Show honest native, adapted, and unsupported adapter capabilities")
|
|
383
|
-
.option("--json", "emit machine-readable JSON")
|
|
384
|
-
.option("--inspect", "also inspect managed component directories on this machine")
|
|
385
|
-
.option("--gaps", "show the evidence-gated backlog for unsupported adapter combinations")
|
|
386
|
-
.action(async (options) => {
|
|
387
|
-
if (options.gaps) {
|
|
388
|
-
if (options.inspect)
|
|
389
|
-
throw new Error("Choose either --inspect or --gaps");
|
|
390
|
-
const gaps = buildAdapterCapabilityGaps();
|
|
391
|
-
return console.log(options.json
|
|
392
|
-
? JSON.stringify(gaps, null, 2)
|
|
393
|
-
: formatAdapterCapabilityGaps());
|
|
394
|
-
}
|
|
395
|
-
if (!options.inspect)
|
|
396
|
-
return console.log(options.json
|
|
397
|
-
? JSON.stringify(ADAPTER_CAPABILITIES, null, 2)
|
|
398
|
-
: formatCapabilityMatrix());
|
|
399
|
-
const inventory = await inspectAgents(await detectAgents());
|
|
400
|
-
if (options.json)
|
|
401
|
-
return console.log(JSON.stringify({ capabilities: ADAPTER_CAPABILITIES, inventory }, null, 2));
|
|
402
|
-
console.log(`${formatCapabilityMatrix()}\n\nLocal managed-component inventory:`);
|
|
403
|
-
for (const item of inventory)
|
|
404
|
-
console.log(formatAgentInventory(item));
|
|
405
|
-
});
|
|
406
305
|
program
|
|
407
306
|
.command("doctor")
|
|
408
307
|
.description("Check agents, skill directories, permissions, and Loadout setup")
|