@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,104 +0,0 @@
1
- import {
2
- addContext,
3
- getCurrentContext,
4
- listContexts,
5
- removeContext,
6
- useContext,
7
- type ResolvedContext,
8
- } from "../context";
9
- import {
10
- emitSuccess,
11
- renderTable,
12
- renderContextBanner,
13
- unknownSubcommandError,
14
- } from "../output";
15
-
16
- export async function handleContextCommand(
17
- sub: string,
18
- args: string[],
19
- ctx: { context: ResolvedContext; isJson: boolean },
20
- ) {
21
- if (sub === "list") {
22
- const contexts = await listContexts();
23
- emitSuccess({ isJson: ctx.isJson, context: ctx.context }, contexts, () => {
24
- renderContextBanner(ctx.context);
25
- renderTable(
26
- [
27
- { key: "name", header: "NAME" },
28
- { key: "server", header: "SERVER" },
29
- { key: "token_env", header: "TOKEN_ENV" },
30
- { key: "isDefault", header: "DEFAULT" },
31
- ],
32
- contexts.map((c) => ({
33
- ...c,
34
- token_env: c.token_env ?? "-",
35
- isDefault: c.isDefault ? "*" : "",
36
- })),
37
- );
38
- });
39
- return;
40
- }
41
-
42
- if (sub === "current") {
43
- const current = await getCurrentContext();
44
- emitSuccess({ isJson: ctx.isJson, context: ctx.context }, current, () => {
45
- renderContextBanner(ctx.context);
46
- console.log(`Current context: ${current.name} (${current.server})`);
47
- });
48
- return;
49
- }
50
-
51
- if (sub === "add") {
52
- const name = args[0];
53
- let server: string | undefined;
54
- let tokenEnv: string | undefined;
55
- for (let i = 1; i < args.length; i++) {
56
- if (args[i] === "--server") server = args[++i];
57
- else if (args[i] === "--token-env") tokenEnv = args[++i];
58
- }
59
- if (!name || !server) {
60
- throw new Error(
61
- "usage: skillmux context add <name> --server <url> [--token-env <env_name>]",
62
- );
63
- }
64
- await addContext(name, { server, token_env: tokenEnv });
65
- emitSuccess(
66
- { isJson: ctx.isJson, context: ctx.context },
67
- { name, server, token_env: tokenEnv },
68
- () => {
69
- console.log(`Added context "${name}" -> ${server}`);
70
- },
71
- );
72
- return;
73
- }
74
-
75
- if (sub === "use") {
76
- const name = args[0];
77
- if (!name) throw new Error("usage: skillmux context use <name>");
78
- await useContext(name);
79
- emitSuccess(
80
- { isJson: ctx.isJson, context: ctx.context },
81
- { default_context: name },
82
- () => {
83
- console.log(`Switched default context to "${name}"`);
84
- },
85
- );
86
- return;
87
- }
88
-
89
- if (sub === "remove") {
90
- const name = args[0];
91
- if (!name) throw new Error("usage: skillmux context remove <name>");
92
- await removeContext(name);
93
- emitSuccess(
94
- { isJson: ctx.isJson, context: ctx.context },
95
- { removed: name },
96
- () => {
97
- console.log(`Removed context "${name}"`);
98
- },
99
- );
100
- return;
101
- }
102
-
103
- throw unknownSubcommandError("context", sub, ["add", "list", "current", "use", "remove"]);
104
- }
@@ -1,56 +0,0 @@
1
- import { expandHome } from "../config";
2
- import { pinCore, unpinCore, validateManifest, writeManifestAtomic } from "../manifest";
3
- import { emitSuccess, unknownSubcommandError } from "../output";
4
- import { confirmIfNeeded, loadManifestContext } from "./shared";
5
- export async function runCore(
6
- subCommand: string,
7
- args: string[],
8
- options: { isJson: boolean; dryRun: boolean },
9
- ): Promise<void> {
10
- if (subCommand !== "pin" && subCommand !== "unpin") {
11
- throw unknownSubcommandError("core", subCommand, ["pin", "unpin"]);
12
- }
13
- const skillIds = args.filter((arg) => !arg.startsWith("-"));
14
- if (skillIds.length === 0) {
15
- throw new Error(`usage: skillmux core ${subCommand} <skill_id>... --yes`);
16
- }
17
- const yes = args.includes("--yes");
18
- const { config, vaultPath, manifestPath, manifest } =
19
- await loadManifestContext();
20
- let updated = manifest;
21
- for (const skillId of skillIds) {
22
- updated =
23
- subCommand === "pin"
24
- ? pinCore(updated, skillId)
25
- : unpinCore(updated, skillId);
26
- }
27
- validateManifest(
28
- updated,
29
- vaultPath,
30
- config.local_vault_paths.map(expandHome),
31
- );
32
- if (options.dryRun) {
33
- emitSuccess(
34
- { isJson: options.isJson },
35
- { subcommand: subCommand, skill_ids: skillIds },
36
- () =>
37
- console.log(`${subCommand}: [core] ${skillIds.join(", ")} (dry-run)`),
38
- );
39
- return;
40
- }
41
- if (
42
- !(await confirmIfNeeded({
43
- confirmed: yes,
44
- isJson: options.isJson,
45
- prompt: `${subCommand} ${skillIds.join(", ")} in [core]?`,
46
- nonInteractiveError: `skillmux core ${subCommand} requires --yes when run non-interactively`,
47
- }))
48
- )
49
- return;
50
- writeManifestAtomic(manifestPath, updated);
51
- emitSuccess(
52
- { isJson: options.isJson },
53
- { subcommand: subCommand, skill_ids: skillIds },
54
- () => console.log(`${subCommand}: [core] ${skillIds.join(", ")}`),
55
- );
56
- }
@@ -1,97 +0,0 @@
1
- import { resolveConfigPath } from "../config";
2
- import { diagnose } from "../doctor";
3
- import { getEffectiveConfig } from "../config-service";
4
- import type { ContextAdapter } from "../adapters";
5
- import type { ResolvedContext } from "../context";
6
- import { isGlobalFlag, isGlobalFlagWithValue } from "../global-flags";
7
- import {
8
- emitSuccess,
9
- green,
10
- red,
11
- renderContextBanner,
12
- } from "../output";
13
-
14
- /**
15
- * doctor takes no options of its own, but it still has to reject unknown ones
16
- * rather than silently ignoring them the way every other command does.
17
- */
18
- export function parseDoctorArgs(args: readonly string[]): void {
19
- for (let i = 0; i < args.length; i++) {
20
- const option = args[i];
21
- if (isGlobalFlag(option, "--json", "--allow-insecure", "--verbose")) {
22
- // handled globally by main(); recognized here so it isn't rejected
23
- } else if (isGlobalFlagWithValue(option)) {
24
- // handled globally by main()'s resolveContext(); skip its value too
25
- i++;
26
- } else {
27
- throw new Error(`unknown doctor option: ${option}`);
28
- }
29
- }
30
- }
31
-
32
- export async function runDoctor(options: {
33
- isJson: boolean;
34
- context: ResolvedContext;
35
- adapter: ContextAdapter;
36
- args?: readonly string[];
37
- }): Promise<void> {
38
- parseDoctorArgs(options.args ?? []);
39
- if (options.context.type === "remote") {
40
- const context = options.context;
41
- const [status, caps] = await Promise.all([
42
- options.adapter.configStatus(),
43
- options.adapter.getCapabilities(),
44
- ]);
45
- const remoteReport = {
46
- target: context.name || context.server,
47
- server: context.server,
48
- version: status.version,
49
- deployment_runtime: status.deployment_runtime,
50
- image_variant: status.image_variant ?? null,
51
- runtime: status.runtime,
52
- readiness: status.readiness,
53
- active_revision: status.active_revision,
54
- capabilities: caps,
55
- restart_required_keys: status.restart_required_keys,
56
- last_reload_error: status.last_reload_error,
57
- };
58
- emitSuccess({ isJson: options.isJson, target: options.context }, remoteReport, () => {
59
- renderContextBanner(options.context);
60
- console.log(`server: ${remoteReport.server}`);
61
- console.log(`version: ${remoteReport.version}`);
62
- console.log(`deployment runtime: ${remoteReport.deployment_runtime}`);
63
- console.log(`image variant: ${remoteReport.image_variant ?? "none"}`);
64
- console.log(`runtime: ${remoteReport.runtime}`);
65
- console.log(`readiness: ${remoteReport.readiness.status} (${remoteReport.readiness.capability})`);
66
- console.log(`active revision: ${remoteReport.active_revision}`);
67
- console.log(`persistence: ${caps.persistence}`);
68
- console.log(`config read: ${caps.config_read}`);
69
- console.log(`config write: ${caps.config_write}`);
70
- if (status.last_reload_error) {
71
- console.log(`last reload error: ${status.last_reload_error}`);
72
- }
73
- if (status.restart_required_keys.length > 0) {
74
- console.log(`restart required for: ${status.restart_required_keys.join(", ")}`);
75
- }
76
- });
77
- return;
78
- }
79
-
80
- const effective = await getEffectiveConfig(resolveConfigPath());
81
- const report = await diagnose(effective.effective, process.env, effective.sources);
82
- emitSuccess({ isJson: options.isJson }, report, () => {
83
- console.log(`version: ${report.version}`);
84
- console.log(`runtime: ${report.runtime}`);
85
- console.log(`image variant: ${report.image_variant ?? "none"}`);
86
- console.log(`vault path: ${report.vault_path}`);
87
- console.log(`state directory: ${report.state_dir}`);
88
- console.log(`inference mode: ${report.mode}`);
89
- console.log(`routing capability: ${report.capability}`);
90
- console.log(`retrieval capability: ${report.retrieval_capability}`);
91
- for (const check of report.checks)
92
- console.log(
93
- `${check.ok ? green("ok") : red("fail")}: ${check.name} - ${check.detail}`,
94
- );
95
- });
96
- if (report.checks.some((check) => !check.ok)) process.exitCode = 1;
97
- }
@@ -1,88 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { expandHome, loadConfig } from "../config";
4
- import { excludeExistingCases, parseEvalCases } from "../eval";
5
- import { emitSuccess, warn } from "../output";
6
- import { parseSince } from "../stats";
7
- import { confirmIfNeeded } from "./shared";
8
- import type { ContextAdapter } from "../adapters";
9
- import { isGlobalFlag, isGlobalFlagWithValue } from "../global-flags";
10
-
11
- export async function runEvalPromote(
12
- args: string[],
13
- options: { isJson: boolean; dryRun: boolean; adapter: ContextAdapter },
14
- ): Promise<void> {
15
- let since: string | undefined;
16
- let out: string | undefined;
17
- let target: string | undefined;
18
- let dryRun = options.dryRun;
19
- let yes = false;
20
- for (let i = 0; i < args.length; i++) {
21
- const arg = args[i];
22
- if (arg === "--since") since = args[++i];
23
- else if (arg === "--out") out = args[++i];
24
- else if (arg === "--target") target = args[++i];
25
- else if (arg === "--dry-run") dryRun = true;
26
- else if (arg === "--yes") yes = true;
27
- else if (isGlobalFlag(arg, "--json", "--allow-insecure", "--verbose")) {
28
- // handled globally by main()'s isJson flag; recognized here so it isn't rejected
29
- } else if (isGlobalFlagWithValue(arg)) {
30
- i++; // skip flag value
31
- } else if (arg?.startsWith("--")) {
32
- throw new Error(`unknown eval promote option: ${arg}`);
33
- }
34
- }
35
- if (!since) {
36
- throw new Error("usage: skillmux eval promote --since <window> [--out <path>] [--dry-run] [--yes] [--json]");
37
- }
38
-
39
- if (out !== undefined && target !== undefined && out !== target) {
40
- throw new Error("cannot specify conflicting --out and --target");
41
- }
42
-
43
- if (target !== undefined) {
44
- warn("--target is deprecated, use --out instead");
45
- }
46
-
47
- const config = await loadConfig();
48
- const stateDir = expandHome(config.state_dir);
49
- const targetPath = out ?? target ?? join(stateDir, "eval-observed.json");
50
- const sinceDate = parseSince(since);
51
- const sinceIso = sinceDate.toISOString();
52
-
53
- const candidates = await options.adapter.evalPromote(since);
54
-
55
- const existing = existsSync(targetPath) ? parseEvalCases(JSON.parse(readFileSync(targetPath, "utf-8"))) : [];
56
- const { cases: newCases, skipped } = excludeExistingCases(candidates, existing);
57
-
58
- warn("promoted eval cases contain raw user queries");
59
-
60
- if (dryRun) {
61
- emitSuccess(
62
- { isJson: options.isJson },
63
- { dry_run: true, since: sinceIso, target_path: targetPath, promoted: newCases.length, skipped_existing: skipped },
64
- () => console.log(`promote: would write ${newCases.length} case(s) to ${targetPath} (skipped_existing=${skipped})`),
65
- );
66
- return;
67
- }
68
-
69
- if (
70
- !(await confirmIfNeeded({
71
- confirmed: yes,
72
- isJson: options.isJson,
73
- prompt: `promote ${newCases.length} eval case(s) to ${targetPath}?`,
74
- nonInteractiveError: "skillmux eval promote requires --yes when run non-interactively",
75
- }))
76
- )
77
- return;
78
-
79
- if (newCases.length > 0) {
80
- writeFileSync(targetPath, JSON.stringify([...existing, ...newCases], null, 2) + "\n");
81
- }
82
-
83
- emitSuccess(
84
- { isJson: options.isJson },
85
- { dry_run: false, since: sinceIso, target_path: targetPath, promoted: newCases.length, skipped_existing: skipped },
86
- () => console.log(`promote: wrote ${newCases.length} case(s) to ${targetPath} (skipped_existing=${skipped})`),
87
- );
88
- }