@klhapp/skillmux 1.12.0 → 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 (103) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +18 -9
  3. package/bin/skillmux.js +89 -0
  4. package/docs/README.md +4 -4
  5. package/docs/cli.md +14 -4
  6. package/docs/concepts.md +2 -2
  7. package/docs/configuration.md +18 -3
  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/troubleshooting.md +18 -2
  39. package/package.json +15 -8
  40. package/src/adapters.ts +0 -438
  41. package/src/audit.ts +0 -21
  42. package/src/cli.ts +0 -928
  43. package/src/clients.ts +0 -368
  44. package/src/commands/audit.ts +0 -85
  45. package/src/commands/config.ts +0 -224
  46. package/src/commands/context.ts +0 -104
  47. package/src/commands/core.ts +0 -56
  48. package/src/commands/doctor.ts +0 -97
  49. package/src/commands/eval.ts +0 -88
  50. package/src/commands/init.ts +0 -672
  51. package/src/commands/install.ts +0 -155
  52. package/src/commands/local-vault.ts +0 -60
  53. package/src/commands/models.ts +0 -10
  54. package/src/commands/outdated.ts +0 -115
  55. package/src/commands/project.ts +0 -580
  56. package/src/commands/report.ts +0 -66
  57. package/src/commands/scan.ts +0 -71
  58. package/src/commands/shared.ts +0 -38
  59. package/src/commands/skill.ts +0 -33
  60. package/src/commands/sync.ts +0 -233
  61. package/src/commands/target.ts +0 -225
  62. package/src/commands/update.ts +0 -277
  63. package/src/completions.ts +0 -196
  64. package/src/concurrency-limiter.ts +0 -61
  65. package/src/config-service.ts +0 -398
  66. package/src/config-watcher.ts +0 -253
  67. package/src/config.ts +0 -583
  68. package/src/context.ts +0 -188
  69. package/src/db-audit.ts +0 -286
  70. package/src/db-index.ts +0 -238
  71. package/src/db.ts +0 -3
  72. package/src/deployment.ts +0 -39
  73. package/src/doctor.ts +0 -205
  74. package/src/eval.ts +0 -302
  75. package/src/global-flags.ts +0 -46
  76. package/src/init-agents.ts +0 -329
  77. package/src/init-instructions.ts +0 -192
  78. package/src/init.ts +0 -390
  79. package/src/install.ts +0 -232
  80. package/src/lifecycle.ts +0 -51
  81. package/src/logger.ts +0 -26
  82. package/src/manifest.ts +0 -359
  83. package/src/mcp-registration.ts +0 -89
  84. package/src/metrics.ts +0 -121
  85. package/src/models.ts +0 -20
  86. package/src/output.ts +0 -214
  87. package/src/project-setup.ts +0 -36
  88. package/src/prompts.ts +0 -124
  89. package/src/provenance.ts +0 -99
  90. package/src/rate-limiter.ts +0 -137
  91. package/src/readiness.ts +0 -30
  92. package/src/redact.ts +0 -52
  93. package/src/router-core.ts +0 -666
  94. package/src/rrf.ts +0 -31
  95. package/src/scan.ts +0 -300
  96. package/src/server.ts +0 -930
  97. package/src/setup.ts +0 -145
  98. package/src/snapshot.ts +0 -135
  99. package/src/stats.ts +0 -340
  100. package/src/sync.ts +0 -482
  101. package/src/toml-writer.ts +0 -51
  102. package/src/types.ts +0 -212
  103. 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
- }