@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.
Files changed (104) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +20 -10
  3. package/bin/skillmux.js +89 -0
  4. package/docs/README.md +4 -4
  5. package/docs/cli.md +15 -5
  6. package/docs/concepts.md +4 -3
  7. package/docs/configuration.md +22 -5
  8. package/docs/deployment.md +3 -3
  9. package/docs/getting-started.md +32 -7
  10. package/docs/releasing.md +17 -2
  11. package/docs/sdd/cli-remote-target-parity/schema-bucket-b.yaml +297 -0
  12. package/docs/sdd/cli-remote-target-parity/spec-bucket-b.md +95 -0
  13. package/docs/sdd/cli-remote-target-parity/spec.md +111 -0
  14. package/docs/sdd/cli-remote-target-parity/think.md +183 -0
  15. package/docs/sdd/cli-surface-consistency/think.md +149 -0
  16. package/docs/sdd/config-authority-rerank-resilience/plan.md +338 -0
  17. package/docs/sdd/config-authority-rerank-resilience/review.md +124 -0
  18. package/docs/sdd/config-authority-rerank-resilience/spec.md +78 -0
  19. package/docs/sdd/feat-packaging-deployment/review.md +95 -0
  20. package/docs/sdd/fetch-outcome-flywheel/review.md +103 -0
  21. package/docs/sdd/fetch-outcome-flywheel/schema.json +347 -0
  22. package/docs/sdd/fetch-outcome-flywheel/spec.md +185 -0
  23. package/docs/sdd/npm-native-binaries/review.md +103 -0
  24. package/docs/sdd/npm-native-binaries/spec.md +148 -0
  25. package/docs/sdd/ranked-shortlist-2/pr3-calibration-removal.md +56 -0
  26. package/docs/sdd/refactor-builtin-target-paths/spec.md +41 -0
  27. package/docs/sdd/runtime-resource-hardening/review.md +86 -0
  28. package/docs/sdd/runtime-resource-hardening/spec.md +43 -0
  29. package/docs/sdd/security-hardening-phase/think.md +129 -0
  30. package/docs/sdd/security-observability/review.md +96 -0
  31. package/docs/sdd/security-observability/spec.md +55 -0
  32. package/docs/sdd/skill-provenance-update/review.md +152 -0
  33. package/docs/sdd/skill-provenance-update/schema.json +193 -0
  34. package/docs/sdd/skill-provenance-update/spec.md +134 -0
  35. package/docs/sdd/supply-chain-hardening/review.md +50 -0
  36. package/docs/sdd/supply-chain-hardening/spec.md +42 -0
  37. package/docs/sdd/target-marker-rehome/spec.md +33 -0
  38. package/docs/skill-management.md +1 -1
  39. package/docs/troubleshooting.md +18 -2
  40. package/package.json +15 -8
  41. package/src/adapters.ts +0 -438
  42. package/src/audit.ts +0 -21
  43. package/src/cli.ts +0 -928
  44. package/src/clients.ts +0 -368
  45. package/src/commands/audit.ts +0 -85
  46. package/src/commands/config.ts +0 -224
  47. package/src/commands/context.ts +0 -104
  48. package/src/commands/core.ts +0 -56
  49. package/src/commands/doctor.ts +0 -97
  50. package/src/commands/eval.ts +0 -88
  51. package/src/commands/init.ts +0 -672
  52. package/src/commands/install.ts +0 -155
  53. package/src/commands/local-vault.ts +0 -60
  54. package/src/commands/models.ts +0 -10
  55. package/src/commands/outdated.ts +0 -115
  56. package/src/commands/project.ts +0 -580
  57. package/src/commands/report.ts +0 -66
  58. package/src/commands/scan.ts +0 -71
  59. package/src/commands/shared.ts +0 -38
  60. package/src/commands/skill.ts +0 -33
  61. package/src/commands/sync.ts +0 -233
  62. package/src/commands/target.ts +0 -225
  63. package/src/commands/update.ts +0 -277
  64. package/src/completions.ts +0 -196
  65. package/src/concurrency-limiter.ts +0 -61
  66. package/src/config-service.ts +0 -398
  67. package/src/config-watcher.ts +0 -253
  68. package/src/config.ts +0 -583
  69. package/src/context.ts +0 -188
  70. package/src/db-audit.ts +0 -286
  71. package/src/db-index.ts +0 -238
  72. package/src/db.ts +0 -3
  73. package/src/deployment.ts +0 -39
  74. package/src/doctor.ts +0 -205
  75. package/src/eval.ts +0 -302
  76. package/src/global-flags.ts +0 -46
  77. package/src/init-agents.ts +0 -329
  78. package/src/init-instructions.ts +0 -192
  79. package/src/init.ts +0 -389
  80. package/src/install.ts +0 -232
  81. package/src/lifecycle.ts +0 -51
  82. package/src/logger.ts +0 -26
  83. package/src/manifest.ts +0 -352
  84. package/src/mcp-registration.ts +0 -89
  85. package/src/metrics.ts +0 -121
  86. package/src/models.ts +0 -20
  87. package/src/output.ts +0 -214
  88. package/src/project-setup.ts +0 -36
  89. package/src/prompts.ts +0 -124
  90. package/src/provenance.ts +0 -99
  91. package/src/rate-limiter.ts +0 -137
  92. package/src/readiness.ts +0 -30
  93. package/src/redact.ts +0 -52
  94. package/src/router-core.ts +0 -666
  95. package/src/rrf.ts +0 -31
  96. package/src/scan.ts +0 -300
  97. package/src/server.ts +0 -930
  98. package/src/setup.ts +0 -145
  99. package/src/snapshot.ts +0 -135
  100. package/src/stats.ts +0 -340
  101. package/src/sync.ts +0 -482
  102. package/src/toml-writer.ts +0 -51
  103. package/src/types.ts +0 -212
  104. package/src/vault.ts +0 -207
@@ -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
- }
@@ -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
- }
@@ -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
- }