@klhapp/skillmux 0.2.0 → 0.4.2
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 +70 -0
- package/README.md +79 -7
- package/config.example.toml +5 -0
- package/docs/configuration.md +58 -4
- package/docs/releasing.md +53 -32
- package/package.json +1 -1
- package/src/adapters.ts +381 -0
- package/src/calibrate.ts +797 -0
- package/src/cli.ts +866 -385
- package/src/clients.ts +3 -1
- package/src/completions.ts +84 -0
- package/src/config-service.ts +408 -0
- package/src/config-watcher.ts +179 -0
- package/src/config.ts +7 -0
- package/src/context.ts +183 -0
- package/src/dataset-generator.ts +140 -0
- package/src/doctor.ts +43 -0
- package/src/eval.ts +22 -4
- package/src/init.ts +1 -1
- package/src/manifest.ts +124 -10
- package/src/output.ts +151 -0
- package/src/router-core.ts +60 -12
- package/src/server.ts +119 -0
- package/src/snapshot.ts +135 -0
- package/src/sync.ts +29 -8
- package/src/types.ts +7 -0
- package/src/vault.ts +59 -1
package/src/types.ts
CHANGED
|
@@ -83,16 +83,23 @@ export interface RateLimitConfig {
|
|
|
83
83
|
trust_proxy?: boolean;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
export interface AdminConfig {
|
|
87
|
+
enabled: boolean;
|
|
88
|
+
token_env: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
export interface ServerConfig {
|
|
87
92
|
auth_enabled: boolean;
|
|
88
93
|
auth_token_env: string;
|
|
89
94
|
allowed_origins: string[];
|
|
90
95
|
hostname?: string;
|
|
91
96
|
rate_limit?: RateLimitConfig;
|
|
97
|
+
admin?: AdminConfig;
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
export interface Config {
|
|
95
101
|
vault_path: string;
|
|
102
|
+
local_vault_paths: string[];
|
|
96
103
|
state_dir: string;
|
|
97
104
|
recall: RecallConfig;
|
|
98
105
|
thresholds: Thresholds;
|
package/src/vault.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readdirSync, statSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
|
|
4
4
|
export const SKILL_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,127}$/;
|
|
@@ -66,6 +66,64 @@ export async function scanVault(
|
|
|
66
66
|
return skills;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/** Local overlays checked first (in order), canonical vault_path is the fallback. */
|
|
70
|
+
export function vaultResolutionOrder(vaultPath: string, localVaultPaths: string[]): string[] {
|
|
71
|
+
return [...localVaultPaths, vaultPath];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Which configured root actually backs skillId, per local-overrides-first precedence. */
|
|
75
|
+
export function resolveSkillRoot(skillId: string, vaultPath: string, localVaultPaths: string[]): string | null {
|
|
76
|
+
for (const root of vaultResolutionOrder(vaultPath, localVaultPaths)) {
|
|
77
|
+
if (existsSync(join(root, skillId, "SKILL.md"))) return root;
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Scans every configured root and merges by skill_id — first-seen (per resolution order) wins. */
|
|
83
|
+
export async function scanVaults(
|
|
84
|
+
vaultPath: string,
|
|
85
|
+
localVaultPaths: string[],
|
|
86
|
+
onInvalid?: (skillId: string, error: unknown) => void,
|
|
87
|
+
): Promise<VaultSkill[]> {
|
|
88
|
+
const seen = new Set<string>();
|
|
89
|
+
const merged: VaultSkill[] = [];
|
|
90
|
+
for (const root of vaultResolutionOrder(vaultPath, localVaultPaths)) {
|
|
91
|
+
if (!existsSync(root)) continue;
|
|
92
|
+
for (const skill of await scanVault(root, onInvalid)) {
|
|
93
|
+
if (seen.has(skill.skill_id)) continue;
|
|
94
|
+
seen.add(skill.skill_id);
|
|
95
|
+
merged.push(skill);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return merged;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ShadowedSkill {
|
|
102
|
+
skill_id: string;
|
|
103
|
+
winner: string;
|
|
104
|
+
shadowed: string[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** skill_ids present in more than one configured root — every root after the winner is silently shadowed. */
|
|
108
|
+
export function findShadowedSkills(vaultPath: string, localVaultPaths: string[]): ShadowedSkill[] {
|
|
109
|
+
const rootsBySkillId = new Map<string, string[]>();
|
|
110
|
+
for (const root of vaultResolutionOrder(vaultPath, localVaultPaths)) {
|
|
111
|
+
if (!existsSync(root)) continue;
|
|
112
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
113
|
+
if (!entry.isDirectory() || !SKILL_ID_PATTERN.test(entry.name)) continue;
|
|
114
|
+
if (!existsSync(join(root, entry.name, "SKILL.md"))) continue;
|
|
115
|
+
const roots = rootsBySkillId.get(entry.name) ?? [];
|
|
116
|
+
roots.push(root);
|
|
117
|
+
rootsBySkillId.set(entry.name, roots);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const shadowed: ShadowedSkill[] = [];
|
|
121
|
+
for (const [skillId, roots] of rootsBySkillId) {
|
|
122
|
+
if (roots.length > 1) shadowed.push({ skill_id: skillId, winner: roots[0]!, shadowed: roots.slice(1) });
|
|
123
|
+
}
|
|
124
|
+
return shadowed.sort((a, b) => a.skill_id.localeCompare(b.skill_id));
|
|
125
|
+
}
|
|
126
|
+
|
|
69
127
|
/** Relative paths of everything under the skill dir except SKILL.md itself, sorted. */
|
|
70
128
|
export function listSupportingFiles(vaultPath: string, skillId: string): string[] {
|
|
71
129
|
const root = join(vaultPath, skillId);
|