@klhapp/skillmux 1.11.2 → 1.13.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 +15 -0
- package/README.md +20 -10
- package/bin/skillmux.js +89 -0
- package/docs/README.md +4 -4
- package/docs/cli.md +15 -5
- package/docs/concepts.md +4 -3
- package/docs/configuration.md +22 -5
- package/docs/deployment.md +3 -3
- package/docs/getting-started.md +32 -7
- package/docs/releasing.md +17 -2
- package/docs/sdd/cli-remote-target-parity/schema-bucket-b.yaml +297 -0
- package/docs/sdd/cli-remote-target-parity/spec-bucket-b.md +95 -0
- package/docs/sdd/cli-remote-target-parity/spec.md +111 -0
- package/docs/sdd/cli-remote-target-parity/think.md +183 -0
- package/docs/sdd/cli-surface-consistency/think.md +149 -0
- package/docs/sdd/config-authority-rerank-resilience/plan.md +338 -0
- package/docs/sdd/config-authority-rerank-resilience/review.md +124 -0
- package/docs/sdd/config-authority-rerank-resilience/spec.md +78 -0
- package/docs/sdd/feat-packaging-deployment/review.md +95 -0
- package/docs/sdd/fetch-outcome-flywheel/review.md +103 -0
- package/docs/sdd/fetch-outcome-flywheel/schema.json +347 -0
- package/docs/sdd/fetch-outcome-flywheel/spec.md +185 -0
- package/docs/sdd/npm-native-binaries/review.md +103 -0
- package/docs/sdd/npm-native-binaries/spec.md +148 -0
- package/docs/sdd/ranked-shortlist-2/pr3-calibration-removal.md +56 -0
- package/docs/sdd/refactor-builtin-target-paths/spec.md +41 -0
- package/docs/sdd/runtime-resource-hardening/review.md +86 -0
- package/docs/sdd/runtime-resource-hardening/spec.md +43 -0
- package/docs/sdd/security-hardening-phase/think.md +129 -0
- package/docs/sdd/security-observability/review.md +96 -0
- package/docs/sdd/security-observability/spec.md +55 -0
- package/docs/sdd/skill-provenance-update/review.md +152 -0
- package/docs/sdd/skill-provenance-update/schema.json +193 -0
- package/docs/sdd/skill-provenance-update/spec.md +134 -0
- package/docs/sdd/supply-chain-hardening/review.md +50 -0
- package/docs/sdd/supply-chain-hardening/spec.md +42 -0
- package/docs/sdd/target-marker-rehome/spec.md +33 -0
- package/docs/skill-management.md +1 -1
- package/docs/troubleshooting.md +18 -2
- package/package.json +15 -8
- package/src/adapters.ts +0 -438
- package/src/audit.ts +0 -21
- package/src/cli.ts +0 -928
- package/src/clients.ts +0 -368
- package/src/commands/audit.ts +0 -85
- package/src/commands/config.ts +0 -224
- package/src/commands/context.ts +0 -104
- package/src/commands/core.ts +0 -56
- package/src/commands/doctor.ts +0 -97
- package/src/commands/eval.ts +0 -88
- package/src/commands/init.ts +0 -672
- package/src/commands/install.ts +0 -155
- package/src/commands/local-vault.ts +0 -60
- package/src/commands/models.ts +0 -10
- package/src/commands/outdated.ts +0 -115
- package/src/commands/project.ts +0 -580
- package/src/commands/report.ts +0 -66
- package/src/commands/scan.ts +0 -71
- package/src/commands/shared.ts +0 -38
- package/src/commands/skill.ts +0 -33
- package/src/commands/sync.ts +0 -233
- package/src/commands/target.ts +0 -225
- package/src/commands/update.ts +0 -277
- package/src/completions.ts +0 -196
- package/src/concurrency-limiter.ts +0 -61
- package/src/config-service.ts +0 -398
- package/src/config-watcher.ts +0 -253
- package/src/config.ts +0 -583
- package/src/context.ts +0 -188
- package/src/db-audit.ts +0 -286
- package/src/db-index.ts +0 -238
- package/src/db.ts +0 -3
- package/src/deployment.ts +0 -39
- package/src/doctor.ts +0 -205
- package/src/eval.ts +0 -302
- package/src/global-flags.ts +0 -46
- package/src/init-agents.ts +0 -329
- package/src/init-instructions.ts +0 -192
- package/src/init.ts +0 -389
- package/src/install.ts +0 -232
- package/src/lifecycle.ts +0 -51
- package/src/logger.ts +0 -26
- package/src/manifest.ts +0 -352
- package/src/mcp-registration.ts +0 -89
- package/src/metrics.ts +0 -121
- package/src/models.ts +0 -20
- package/src/output.ts +0 -214
- package/src/project-setup.ts +0 -36
- package/src/prompts.ts +0 -124
- package/src/provenance.ts +0 -99
- package/src/rate-limiter.ts +0 -137
- package/src/readiness.ts +0 -30
- package/src/redact.ts +0 -52
- package/src/router-core.ts +0 -666
- package/src/rrf.ts +0 -31
- package/src/scan.ts +0 -300
- package/src/server.ts +0 -930
- package/src/setup.ts +0 -145
- package/src/snapshot.ts +0 -135
- package/src/stats.ts +0 -340
- package/src/sync.ts +0 -482
- package/src/toml-writer.ts +0 -51
- package/src/types.ts +0 -212
- package/src/vault.ts +0 -207
package/src/commands/install.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { rmSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { expandHome, loadConfig } from "../config";
|
|
4
|
-
import {
|
|
5
|
-
assertHostAllowed,
|
|
6
|
-
cloneToTemp,
|
|
7
|
-
deriveRepoName,
|
|
8
|
-
installIntoVault,
|
|
9
|
-
isLocalFileUrl,
|
|
10
|
-
resolveCloneCommit,
|
|
11
|
-
resolveRepoSource,
|
|
12
|
-
resolveSkillDir,
|
|
13
|
-
validateSkillCandidate,
|
|
14
|
-
} from "../install";
|
|
15
|
-
import { emitSuccess } from "../output";
|
|
16
|
-
import { hashSkillContent, writeSkillOrigin } from "../provenance";
|
|
17
|
-
import {
|
|
18
|
-
FAIL_ON_USAGE,
|
|
19
|
-
parseFailOn,
|
|
20
|
-
renderScanText,
|
|
21
|
-
resolveMutatingFailOn,
|
|
22
|
-
scanExitCode,
|
|
23
|
-
type FailOnOption,
|
|
24
|
-
} from "../scan";
|
|
25
|
-
import { isGlobalFlag } from "../global-flags";
|
|
26
|
-
import { confirmIfNeeded } from "./shared";
|
|
27
|
-
|
|
28
|
-
function parseInstallArgs(args: string[]): {
|
|
29
|
-
repo?: string;
|
|
30
|
-
force: boolean;
|
|
31
|
-
dryRun: boolean;
|
|
32
|
-
failOn?: FailOnOption;
|
|
33
|
-
allowLocalSource: boolean;
|
|
34
|
-
yes: boolean;
|
|
35
|
-
} {
|
|
36
|
-
let repo: string | undefined;
|
|
37
|
-
let force = false;
|
|
38
|
-
let dryRun = false;
|
|
39
|
-
let failOn: FailOnOption | undefined;
|
|
40
|
-
let allowLocalSource = false;
|
|
41
|
-
let yes = false;
|
|
42
|
-
for (let i = 0; i < args.length; i++) {
|
|
43
|
-
const option = args[i];
|
|
44
|
-
if (option === "--force") force = true;
|
|
45
|
-
else if (option === "--yes") yes = true;
|
|
46
|
-
else if (option === "--dry-run") dryRun = true;
|
|
47
|
-
else if (option === "--allow-local-source") allowLocalSource = true;
|
|
48
|
-
else if (option === "--fail-on") {
|
|
49
|
-
failOn = parseFailOn(args[++i]);
|
|
50
|
-
} else if (isGlobalFlag(option, "--json", "--verbose")) {
|
|
51
|
-
// handled globally by main()'s isJson/isVerbose flags; recognized here so they aren't rejected
|
|
52
|
-
} else if (option?.startsWith("--")) {
|
|
53
|
-
throw new Error(`unknown install option: ${option}`);
|
|
54
|
-
} else if (repo !== undefined) {
|
|
55
|
-
throw new Error("skillmux install accepts at most one <repo> argument");
|
|
56
|
-
} else {
|
|
57
|
-
repo = option;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return { repo, force, dryRun, failOn, allowLocalSource, yes };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export async function runInstall(
|
|
64
|
-
args: string[],
|
|
65
|
-
options: { isJson: boolean },
|
|
66
|
-
): Promise<void> {
|
|
67
|
-
const { repo, force, dryRun, failOn, allowLocalSource, yes } = parseInstallArgs(args);
|
|
68
|
-
if (!repo) {
|
|
69
|
-
throw new Error(
|
|
70
|
-
`usage: skillmux install <repo>[/path] [--yes] [--force] [--fail-on ${FAIL_ON_USAGE}] [--dry-run] [--allow-local-source] [--json]`,
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const source = resolveRepoSource(repo);
|
|
75
|
-
if (!allowLocalSource && isLocalFileUrl(source.url)) {
|
|
76
|
-
throw new Error(
|
|
77
|
-
`"${repo}" is a local (file://) source — pass --allow-local-source to install from it`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
const config = await loadConfig();
|
|
81
|
-
assertHostAllowed(source.url, config.egress?.allowed_hosts);
|
|
82
|
-
const cloneDir = await cloneToTemp(source.url);
|
|
83
|
-
try {
|
|
84
|
-
const resolved = resolveSkillDir(
|
|
85
|
-
cloneDir,
|
|
86
|
-
deriveRepoName(source.url),
|
|
87
|
-
source.skillPath,
|
|
88
|
-
);
|
|
89
|
-
const { findings } = await validateSkillCandidate(
|
|
90
|
-
resolved.skillId,
|
|
91
|
-
resolved.dir,
|
|
92
|
-
);
|
|
93
|
-
if (!options.isJson) console.log(renderScanText({ scanned: 1, findings }));
|
|
94
|
-
|
|
95
|
-
// Defaults to blocking on a high-severity finding. Printing the findings
|
|
96
|
-
// and installing anyway is the wrong default for the command that pulls
|
|
97
|
-
// remote content into the vault.
|
|
98
|
-
const effectiveFailOn = resolveMutatingFailOn(failOn);
|
|
99
|
-
if (scanExitCode(findings, effectiveFailOn) !== 0) {
|
|
100
|
-
process.exitCode = 1;
|
|
101
|
-
console.error(
|
|
102
|
-
`aborting install: a finding met the --fail-on ${effectiveFailOn} threshold` +
|
|
103
|
-
(failOn === undefined ? " (the default)" : "") +
|
|
104
|
-
`; re-run with --fail-on none to install anyway`,
|
|
105
|
-
);
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const vaultPath = expandHome(config.vault_path);
|
|
110
|
-
if (dryRun) {
|
|
111
|
-
const plannedPath = join(vaultPath, resolved.skillId);
|
|
112
|
-
emitSuccess(
|
|
113
|
-
{ isJson: options.isJson },
|
|
114
|
-
{ skill_id: resolved.skillId, would_install_at: plannedPath },
|
|
115
|
-
() =>
|
|
116
|
-
console.log(
|
|
117
|
-
`dry-run: would install "${resolved.skillId}" into ${plannedPath}`,
|
|
118
|
-
),
|
|
119
|
-
);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Confirm after the scan, so an interactive user decides with the findings
|
|
124
|
-
// already on screen, and after the --dry-run return, which writes nothing.
|
|
125
|
-
const proceed = await confirmIfNeeded({
|
|
126
|
-
confirmed: yes,
|
|
127
|
-
isJson: options.isJson,
|
|
128
|
-
prompt: `install:\n ${resolved.skillId} <- ${source.url}\n?`,
|
|
129
|
-
nonInteractiveError: "skillmux install requires --yes when run non-interactively",
|
|
130
|
-
});
|
|
131
|
-
if (!proceed) return;
|
|
132
|
-
|
|
133
|
-
const commit = resolveCloneCommit(cloneDir);
|
|
134
|
-
const targetDir = installIntoVault(
|
|
135
|
-
vaultPath,
|
|
136
|
-
resolved.skillId,
|
|
137
|
-
resolved.dir,
|
|
138
|
-
force,
|
|
139
|
-
);
|
|
140
|
-
writeSkillOrigin(targetDir, {
|
|
141
|
-
source_url: source.url,
|
|
142
|
-
skill_path: source.skillPath,
|
|
143
|
-
commit,
|
|
144
|
-
installed_at: new Date().toISOString(),
|
|
145
|
-
content_hash: hashSkillContent(targetDir),
|
|
146
|
-
});
|
|
147
|
-
emitSuccess(
|
|
148
|
-
{ isJson: options.isJson },
|
|
149
|
-
{ skill_id: resolved.skillId, installed_at: targetDir },
|
|
150
|
-
() => console.log(`installed "${resolved.skillId}" into ${targetDir}`),
|
|
151
|
-
);
|
|
152
|
-
} finally {
|
|
153
|
-
rmSync(cloneDir, { recursive: true, force: true });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { expandHome, loadConfig } from "../config";
|
|
4
|
-
import { emitSuccess } from "../output";
|
|
5
|
-
import { writeLocalVaultMarker } from "../sync";
|
|
6
|
-
import { confirmIfNeeded } from "./shared";
|
|
7
|
-
|
|
8
|
-
export async function runLocalVaultInit(
|
|
9
|
-
args: string[],
|
|
10
|
-
options: { isJson: boolean; dryRun: boolean },
|
|
11
|
-
): Promise<void> {
|
|
12
|
-
const path = args[0];
|
|
13
|
-
if (!path) throw new Error("usage: skillmux local-vault init <path> --yes");
|
|
14
|
-
const expanded = expandHome(path);
|
|
15
|
-
const config = await loadConfig();
|
|
16
|
-
const localVaultPaths = config.local_vault_paths.map(expandHome);
|
|
17
|
-
if (!localVaultPaths.includes(expanded)) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
`"${path}" is not one of the configured local_vault_paths — add it to config.toml first`,
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
if (!existsSync(expanded)) throw new Error(`"${path}" does not exist`);
|
|
23
|
-
const markerPath = join(expanded, ".skillmux");
|
|
24
|
-
if (options.dryRun) {
|
|
25
|
-
emitSuccess(
|
|
26
|
-
{ isJson: options.isJson },
|
|
27
|
-
{
|
|
28
|
-
marker_path: markerPath,
|
|
29
|
-
vault_path: expandHome(config.vault_path),
|
|
30
|
-
},
|
|
31
|
-
() =>
|
|
32
|
-
console.log(
|
|
33
|
-
`local-vault init: ${markerPath} (role: local_vault, vault_path: ${expandHome(config.vault_path)}) (dry-run)`,
|
|
34
|
-
),
|
|
35
|
-
);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
if (
|
|
39
|
-
!(await confirmIfNeeded({
|
|
40
|
-
confirmed: args.includes("--yes"),
|
|
41
|
-
isJson: options.isJson,
|
|
42
|
-
prompt: `mark ${expanded} as a local_vault (role: local_vault, vault_path: ${expandHome(config.vault_path)})?`,
|
|
43
|
-
nonInteractiveError:
|
|
44
|
-
"skillmux local-vault init requires --yes when run non-interactively",
|
|
45
|
-
}))
|
|
46
|
-
)
|
|
47
|
-
return;
|
|
48
|
-
writeLocalVaultMarker(expanded, expandHome(config.vault_path));
|
|
49
|
-
emitSuccess(
|
|
50
|
-
{ isJson: options.isJson },
|
|
51
|
-
{
|
|
52
|
-
marker_path: markerPath,
|
|
53
|
-
vault_path: expandHome(config.vault_path),
|
|
54
|
-
},
|
|
55
|
-
() =>
|
|
56
|
-
console.log(
|
|
57
|
-
`wrote ${markerPath} (role: local_vault, vault_path: ${expandHome(config.vault_path)})`,
|
|
58
|
-
),
|
|
59
|
-
);
|
|
60
|
-
}
|
package/src/commands/models.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from "../config";
|
|
2
|
-
import { downloadLocalModels } from "../models";
|
|
3
|
-
import { emitSuccess } from "../output";
|
|
4
|
-
|
|
5
|
-
export async function runModelDownload(options: { isJson: boolean }): Promise<void> {
|
|
6
|
-
const cacheDir = await downloadLocalModels(await loadConfig());
|
|
7
|
-
emitSuccess({ isJson: options.isJson }, { cache_dir: cacheDir }, () =>
|
|
8
|
-
console.log(`models ready in ${cacheDir}`),
|
|
9
|
-
);
|
|
10
|
-
}
|
package/src/commands/outdated.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { readdirSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { expandHome, loadConfig } from "../config";
|
|
4
|
-
import { assertHostAllowed, isLocalFileUrl, remoteHeadCommit } from "../install";
|
|
5
|
-
import { emitSuccess } from "../output";
|
|
6
|
-
import { readSkillOrigin } from "../provenance";
|
|
7
|
-
import { SKILL_ID_PATTERN } from "../vault";
|
|
8
|
-
import { isGlobalFlag } from "../global-flags";
|
|
9
|
-
|
|
10
|
-
export interface OutdatedCheckResult {
|
|
11
|
-
skill_id: string;
|
|
12
|
-
source_url: string;
|
|
13
|
-
recorded_commit: string;
|
|
14
|
-
remote_commit: string | null;
|
|
15
|
-
status: "up_to_date" | "outdated" | "check_failed" | "local_source_skipped";
|
|
16
|
-
reason: string | null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function vaultSkillIds(vaultPath: string): string[] {
|
|
20
|
-
return readdirSync(vaultPath, { withFileTypes: true })
|
|
21
|
-
.filter((entry) => entry.isDirectory() && SKILL_ID_PATTERN.test(entry.name))
|
|
22
|
-
.map((entry) => entry.name)
|
|
23
|
-
.sort();
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export async function checkOutdated(
|
|
27
|
-
vaultPath: string,
|
|
28
|
-
options: { allowLocalSource?: boolean; allowedHosts?: string[] } = {},
|
|
29
|
-
): Promise<OutdatedCheckResult[]> {
|
|
30
|
-
const results: OutdatedCheckResult[] = [];
|
|
31
|
-
for (const skillId of vaultSkillIds(vaultPath)) {
|
|
32
|
-
let origin: ReturnType<typeof readSkillOrigin>;
|
|
33
|
-
try {
|
|
34
|
-
origin = readSkillOrigin(join(vaultPath, skillId));
|
|
35
|
-
} catch (error) {
|
|
36
|
-
// A corrupt or unreadable sidecar is this skill's problem alone — never
|
|
37
|
-
// let it abort the check for every other skill in the vault (AC3/AC4's
|
|
38
|
-
// per-skill isolation applies to local parse failures too, not just
|
|
39
|
-
// unreachable remotes).
|
|
40
|
-
results.push({
|
|
41
|
-
skill_id: skillId,
|
|
42
|
-
source_url: "",
|
|
43
|
-
recorded_commit: "",
|
|
44
|
-
remote_commit: null,
|
|
45
|
-
status: "check_failed",
|
|
46
|
-
reason: `.skillmux-origin: ${error instanceof Error ? error.message : String(error)}`,
|
|
47
|
-
});
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (!origin) continue;
|
|
51
|
-
|
|
52
|
-
if (!options.allowLocalSource && isLocalFileUrl(origin.source_url)) {
|
|
53
|
-
results.push({
|
|
54
|
-
skill_id: skillId,
|
|
55
|
-
source_url: origin.source_url,
|
|
56
|
-
recorded_commit: origin.commit,
|
|
57
|
-
remote_commit: null,
|
|
58
|
-
status: "local_source_skipped",
|
|
59
|
-
reason: "source_url is a local file:// path — skipped by default; pass --allow-local-source to check it",
|
|
60
|
-
});
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let remoteCommit: string | null = null;
|
|
65
|
-
let status: OutdatedCheckResult["status"];
|
|
66
|
-
let reason: string | null = null;
|
|
67
|
-
try {
|
|
68
|
-
assertHostAllowed(origin.source_url, options.allowedHosts);
|
|
69
|
-
remoteCommit = await remoteHeadCommit(origin.source_url);
|
|
70
|
-
status = remoteCommit === origin.commit ? "up_to_date" : "outdated";
|
|
71
|
-
} catch (error) {
|
|
72
|
-
status = "check_failed";
|
|
73
|
-
reason = error instanceof Error ? error.message : String(error);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
results.push({
|
|
77
|
-
skill_id: skillId,
|
|
78
|
-
source_url: origin.source_url,
|
|
79
|
-
recorded_commit: origin.commit,
|
|
80
|
-
remote_commit: remoteCommit,
|
|
81
|
-
status,
|
|
82
|
-
reason,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
return results;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export async function runOutdated(args: string[], options: { isJson: boolean }): Promise<void> {
|
|
89
|
-
let allowLocalSource = false;
|
|
90
|
-
for (const arg of args) {
|
|
91
|
-
if (isGlobalFlag(arg, "--json")) continue;
|
|
92
|
-
if (arg === "--allow-local-source") {
|
|
93
|
-
allowLocalSource = true;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
throw new Error(`unknown outdated option: ${arg}`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const config = await loadConfig();
|
|
100
|
-
const vaultPath = expandHome(config.vault_path);
|
|
101
|
-
const skills = await checkOutdated(vaultPath, { allowLocalSource, allowedHosts: config.egress?.allowed_hosts });
|
|
102
|
-
const checksFailed = skills.filter((s) => s.status === "check_failed").length;
|
|
103
|
-
process.exitCode = checksFailed > 0 ? 1 : 0;
|
|
104
|
-
|
|
105
|
-
emitSuccess({ isJson: options.isJson }, { skills, checks_failed: checksFailed }, () => {
|
|
106
|
-
if (skills.length === 0) {
|
|
107
|
-
console.log("outdated: no vault skills carry provenance");
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
for (const s of skills) {
|
|
111
|
-
const suffix = s.status === "check_failed" ? ` — ${s.reason}` : "";
|
|
112
|
-
console.log(`[${s.status}] ${s.skill_id}${suffix}`);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|