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
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
const commands = [
|
|
2
|
-
"guide",
|
|
3
|
-
"advanced",
|
|
4
|
-
"setup",
|
|
5
|
-
"upgrade",
|
|
6
|
-
"init",
|
|
7
|
-
"lock",
|
|
8
|
-
"export",
|
|
9
|
-
"import",
|
|
10
|
-
"audit",
|
|
11
|
-
"create",
|
|
12
|
-
"pack",
|
|
13
|
-
"publish",
|
|
14
|
-
"search",
|
|
15
|
-
"add",
|
|
16
|
-
"unadd",
|
|
17
|
-
"list",
|
|
18
|
-
"scan",
|
|
19
|
-
"reconcile",
|
|
20
|
-
"status",
|
|
21
|
-
"versions",
|
|
22
|
-
"skill-audit",
|
|
23
|
-
"doctor",
|
|
24
|
-
"health",
|
|
25
|
-
"compare",
|
|
26
|
-
"optimize",
|
|
27
|
-
"activate",
|
|
28
|
-
"enable",
|
|
29
|
-
"disable",
|
|
30
|
-
"adopt",
|
|
31
|
-
"library",
|
|
32
|
-
"update",
|
|
33
|
-
"rollback",
|
|
34
|
-
"discover",
|
|
35
|
-
"candidate",
|
|
36
|
-
"review-queue",
|
|
37
|
-
"review",
|
|
38
|
-
"alerts",
|
|
39
|
-
"alert-ignore",
|
|
40
|
-
"alert-pin",
|
|
41
|
-
"alert-unpin",
|
|
42
|
-
"alert-pins",
|
|
43
|
-
"mcp",
|
|
44
|
-
"mcp-recipe",
|
|
45
|
-
"mcp-config",
|
|
46
|
-
"models",
|
|
47
|
-
"credentials",
|
|
48
|
-
"schedule",
|
|
49
|
-
"unschedule",
|
|
50
|
-
"autopilot",
|
|
51
|
-
"tool",
|
|
52
|
-
"report",
|
|
53
|
-
"share",
|
|
54
|
-
"card",
|
|
55
|
-
"compare-loadouts",
|
|
56
|
-
"badge",
|
|
57
|
-
"inspect",
|
|
58
|
-
"evaluate",
|
|
59
|
-
"watch",
|
|
60
|
-
"sandbox-run",
|
|
61
|
-
"codex-mcp-config",
|
|
62
|
-
"plan",
|
|
63
|
-
"install",
|
|
64
|
-
"convert",
|
|
65
|
-
"canary",
|
|
66
|
-
"capabilities",
|
|
67
|
-
"catalog",
|
|
68
|
-
"profiles",
|
|
69
|
-
"recommend",
|
|
70
|
-
"improve",
|
|
71
|
-
"improve-feedback",
|
|
72
|
-
"sync",
|
|
73
|
-
"outcome",
|
|
74
|
-
"outcomes",
|
|
75
|
-
"remove",
|
|
76
|
-
"uninstall",
|
|
77
|
-
"serve",
|
|
78
|
-
"completion",
|
|
79
|
-
];
|
|
80
|
-
const modelCommands = ["status", "set", "verify"];
|
|
81
|
-
const credentialCommands = ["status", "set", "check", "delete"];
|
|
82
|
-
const candidateCommands = ["list", "inspect", "propose"];
|
|
83
|
-
export function renderShellCompletion(shell) {
|
|
84
|
-
const words = commands.join(" ");
|
|
85
|
-
switch (shell) {
|
|
86
|
-
case "bash":
|
|
87
|
-
return `# Loadout command completion
|
|
88
|
-
_loadout() {
|
|
89
|
-
local current="\${COMP_WORDS[COMP_CWORD]}"
|
|
90
|
-
local commands="${words}"
|
|
91
|
-
if [[ COMP_CWORD -eq 1 ]]; then
|
|
92
|
-
COMPREPLY=( $(compgen -W "$commands" -- "$current") )
|
|
93
|
-
elif [[ COMP_CWORD -eq 2 && "\${COMP_WORDS[1]}" == "models" ]]; then
|
|
94
|
-
COMPREPLY=( $(compgen -W "${modelCommands.join(" ")}" -- "$current") )
|
|
95
|
-
elif [[ COMP_CWORD -eq 2 && "\${COMP_WORDS[1]}" == "credentials" ]]; then
|
|
96
|
-
COMPREPLY=( $(compgen -W "${credentialCommands.join(" ")}" -- "$current") )
|
|
97
|
-
elif [[ COMP_CWORD -eq 2 && "\${COMP_WORDS[1]}" == "candidate" ]]; then
|
|
98
|
-
COMPREPLY=( $(compgen -W "${candidateCommands.join(" ")}" -- "$current") )
|
|
99
|
-
fi
|
|
100
|
-
}
|
|
101
|
-
complete -F _loadout loadout
|
|
102
|
-
`;
|
|
103
|
-
case "zsh":
|
|
104
|
-
return `#compdef loadout
|
|
105
|
-
typeset -a commands model_commands credential_commands candidate_commands
|
|
106
|
-
commands=(${commands.map((command) => `'${command}'`).join(" ")})
|
|
107
|
-
model_commands=(${modelCommands.map((command) => `'${command}'`).join(" ")})
|
|
108
|
-
credential_commands=(${credentialCommands.map((command) => `'${command}'`).join(" ")})
|
|
109
|
-
candidate_commands=(${candidateCommands.map((command) => `'${command}'`).join(" ")})
|
|
110
|
-
if (( CURRENT == 2 )); then
|
|
111
|
-
_describe 'command' commands
|
|
112
|
-
elif (( CURRENT == 3 )) && [[ $words[2] == models ]]; then
|
|
113
|
-
_describe 'models command' model_commands
|
|
114
|
-
elif (( CURRENT == 3 )) && [[ $words[2] == credentials ]]; then
|
|
115
|
-
_describe 'credentials command' credential_commands
|
|
116
|
-
elif (( CURRENT == 3 )) && [[ $words[2] == candidate ]]; then
|
|
117
|
-
_describe 'candidate command' candidate_commands
|
|
118
|
-
fi
|
|
119
|
-
`;
|
|
120
|
-
case "fish":
|
|
121
|
-
return [
|
|
122
|
-
...commands.map((command) => `complete -c loadout -f -n '__fish_use_subcommand' -a ${command}`),
|
|
123
|
-
...modelCommands.map((command) => `complete -c loadout -f -n '__fish_seen_subcommand_from models' -a ${command}`),
|
|
124
|
-
...credentialCommands.map((command) => `complete -c loadout -f -n '__fish_seen_subcommand_from credentials' -a ${command}`),
|
|
125
|
-
...candidateCommands.map((command) => `complete -c loadout -f -n '__fish_seen_subcommand_from candidate' -a ${command}`),
|
|
126
|
-
]
|
|
127
|
-
.join("\n")
|
|
128
|
-
.concat("\n");
|
|
129
|
-
case "powershell":
|
|
130
|
-
return `Register-ArgumentCompleter -Native -CommandName loadout -ScriptBlock {
|
|
131
|
-
param($wordToComplete, $commandAst, $cursorPosition)
|
|
132
|
-
$elements = @($commandAst.CommandElements | ForEach-Object { $_.Extent.Text })
|
|
133
|
-
$candidates = if ($elements.Count -ge 2 -and $elements[1] -eq 'models') { @(${modelCommands.map((command) => `'${command}'`).join(", ")}) } elseif ($elements.Count -ge 2 -and $elements[1] -eq 'credentials') { @(${credentialCommands.map((command) => `'${command}'`).join(", ")}) } elseif ($elements.Count -ge 2 -and $elements[1] -eq 'candidate') { @(${candidateCommands.map((command) => `'${command}'`).join(", ")}) } else { @(${commands.map((command) => `'${command}'`).join(", ")}) }
|
|
134
|
-
$candidates | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }
|
|
135
|
-
}
|
|
136
|
-
`;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
export function parseCompletionShell(value) {
|
|
140
|
-
if (["bash", "zsh", "fish", "powershell"].includes(value))
|
|
141
|
-
return value;
|
|
142
|
-
throw new Error("Supported shells: bash, zsh, fish, powershell");
|
|
143
|
-
}
|
package/dist/src/core/handoff.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { writeFileAtomically } from "./atomic-file.js";
|
|
5
|
-
const HANDOFF_DIR = ".handoff";
|
|
6
|
-
const MESSAGES_FILE = "messages.jsonl";
|
|
7
|
-
const PROTOCOL_FILE = "PROTOCOL.md";
|
|
8
|
-
function handoffDir(projectRoot) {
|
|
9
|
-
return join(projectRoot, HANDOFF_DIR);
|
|
10
|
-
}
|
|
11
|
-
function messagesPath(projectRoot) {
|
|
12
|
-
return join(handoffDir(projectRoot), MESSAGES_FILE);
|
|
13
|
-
}
|
|
14
|
-
export async function isHandoffInitialized(projectRoot) {
|
|
15
|
-
try {
|
|
16
|
-
await readFile(join(handoffDir(projectRoot), PROTOCOL_FILE), "utf8");
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
export async function initHandoff(projectRoot) {
|
|
24
|
-
const dir = handoffDir(projectRoot);
|
|
25
|
-
await mkdir(dir, { recursive: true });
|
|
26
|
-
const protocol = [
|
|
27
|
-
"# Handoff Protocol",
|
|
28
|
-
"",
|
|
29
|
-
"This directory enables asynchronous task handoff between AI coding agents.",
|
|
30
|
-
"Generated by `loadout handoff init`. Compatible with the JSONL handoff convention.",
|
|
31
|
-
"",
|
|
32
|
-
"## Message types",
|
|
33
|
-
"",
|
|
34
|
-
"- **task**: assign work to another agent",
|
|
35
|
-
"- **handoff**: transfer in-progress work with context",
|
|
36
|
-
"- **question**: ask another agent for input",
|
|
37
|
-
"- **done**: signal completion",
|
|
38
|
-
"- **status**: progress update",
|
|
39
|
-
"- **error**: report a failure",
|
|
40
|
-
"- **cancel**: withdraw a task",
|
|
41
|
-
"",
|
|
42
|
-
"## Files",
|
|
43
|
-
"",
|
|
44
|
-
"- `messages.jsonl` — append-only message log (one JSON object per line)",
|
|
45
|
-
"- `PROTOCOL.md` — this file",
|
|
46
|
-
"",
|
|
47
|
-
"## Usage",
|
|
48
|
-
"",
|
|
49
|
-
"```",
|
|
50
|
-
"loadout handoff send codex 'write unit tests for auth module'",
|
|
51
|
-
"loadout handoff send claude-code 'review the API changes' --context 'see PR #42'",
|
|
52
|
-
"loadout handoff status",
|
|
53
|
-
"loadout handoff done <message-id>",
|
|
54
|
-
"```",
|
|
55
|
-
"",
|
|
56
|
-
"Agents read this directory on wake. Loadout manages the message log;",
|
|
57
|
-
"each agent's own CLAUDE.md / AGENTS.md can reference .handoff/ for pickup.",
|
|
58
|
-
"",
|
|
59
|
-
].join("\n");
|
|
60
|
-
await writeFileAtomically(join(dir, PROTOCOL_FILE), protocol);
|
|
61
|
-
// Create empty messages file if it doesn't exist
|
|
62
|
-
try {
|
|
63
|
-
await readFile(messagesPath(projectRoot));
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
await writeFile(messagesPath(projectRoot), "", "utf8");
|
|
67
|
-
}
|
|
68
|
-
return dir;
|
|
69
|
-
}
|
|
70
|
-
export async function sendHandoff(projectRoot, to, description, options = {}) {
|
|
71
|
-
if (!(await isHandoffInitialized(projectRoot))) {
|
|
72
|
-
throw new Error("Handoff not initialized. Run `loadout handoff init` first.");
|
|
73
|
-
}
|
|
74
|
-
const message = {
|
|
75
|
-
id: randomUUID().slice(0, 8),
|
|
76
|
-
type: options.type ?? "task",
|
|
77
|
-
from: options.from ?? "user",
|
|
78
|
-
to,
|
|
79
|
-
description,
|
|
80
|
-
...(options.context ? { context: options.context } : {}),
|
|
81
|
-
timestamp: new Date().toISOString(),
|
|
82
|
-
};
|
|
83
|
-
const path = messagesPath(projectRoot);
|
|
84
|
-
const line = JSON.stringify(message) + "\n";
|
|
85
|
-
await writeFile(path, line, { flag: "a" });
|
|
86
|
-
return message;
|
|
87
|
-
}
|
|
88
|
-
export async function markDone(projectRoot, messageId) {
|
|
89
|
-
const messages = await readMessages(projectRoot);
|
|
90
|
-
const original = messages.find((m) => m.id === messageId);
|
|
91
|
-
if (!original)
|
|
92
|
-
throw new Error(`Message '${messageId}' not found`);
|
|
93
|
-
if (original.type === "done")
|
|
94
|
-
throw new Error(`Message '${messageId}' is already done`);
|
|
95
|
-
return sendHandoff(projectRoot, original.from, `Completed: ${original.description}`, {
|
|
96
|
-
from: original.to,
|
|
97
|
-
type: "done",
|
|
98
|
-
context: `Resolves ${messageId}`,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
export async function readMessages(projectRoot) {
|
|
102
|
-
if (!(await isHandoffInitialized(projectRoot)))
|
|
103
|
-
return [];
|
|
104
|
-
try {
|
|
105
|
-
const content = await readFile(messagesPath(projectRoot), "utf8");
|
|
106
|
-
return content
|
|
107
|
-
.split("\n")
|
|
108
|
-
.filter(Boolean)
|
|
109
|
-
.map((line) => JSON.parse(line));
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
return [];
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
export async function getHandoffState(projectRoot) {
|
|
116
|
-
const initialized = await isHandoffInitialized(projectRoot);
|
|
117
|
-
const messages = initialized ? await readMessages(projectRoot) : [];
|
|
118
|
-
const doneIds = new Set(messages
|
|
119
|
-
.filter((m) => m.type === "done")
|
|
120
|
-
.flatMap((m) => {
|
|
121
|
-
const match = m.context?.match(/Resolves (\w+)/);
|
|
122
|
-
return match ? [match[1]] : [];
|
|
123
|
-
}));
|
|
124
|
-
const pending = messages.filter((m) => m.type === "task" && !doneIds.has(m.id));
|
|
125
|
-
const done = messages.filter((m) => m.type === "task" && doneIds.has(m.id));
|
|
126
|
-
return {
|
|
127
|
-
initialized,
|
|
128
|
-
directory: handoffDir(projectRoot),
|
|
129
|
-
messages,
|
|
130
|
-
pending,
|
|
131
|
-
done,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
export function formatHandoffStatus(state) {
|
|
135
|
-
if (!state.initialized)
|
|
136
|
-
return "Handoff not initialized. Run `loadout handoff init`.";
|
|
137
|
-
if (state.messages.length === 0)
|
|
138
|
-
return "No handoff messages yet. Send one with `loadout handoff send <agent> <task>`.";
|
|
139
|
-
const lines = [];
|
|
140
|
-
if (state.pending.length) {
|
|
141
|
-
lines.push(`Pending (${state.pending.length}):`);
|
|
142
|
-
for (const m of state.pending) {
|
|
143
|
-
lines.push(` ${m.id} ${m.from} → ${m.to} ${m.description}`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (state.done.length) {
|
|
147
|
-
if (lines.length)
|
|
148
|
-
lines.push("");
|
|
149
|
-
lines.push(`Done (${state.done.length}):`);
|
|
150
|
-
for (const m of state.done) {
|
|
151
|
-
lines.push(` ${m.id} ${m.from} → ${m.to} ${m.description}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
const other = state.messages.filter((m) => m.type !== "task" && m.type !== "done");
|
|
155
|
-
if (other.length) {
|
|
156
|
-
if (lines.length)
|
|
157
|
-
lines.push("");
|
|
158
|
-
lines.push(`Other messages: ${other.length}`);
|
|
159
|
-
}
|
|
160
|
-
return lines.join("\n");
|
|
161
|
-
}
|
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.
|