@klhapp/skillmux 1.11.2 → 1.12.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 +7 -0
- package/README.md +2 -1
- package/docs/cli.md +1 -1
- package/docs/concepts.md +2 -1
- package/docs/configuration.md +5 -3
- package/docs/skill-management.md +1 -1
- package/package.json +1 -1
- package/src/init.ts +3 -2
- package/src/manifest.ts +13 -6
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.12.0](https://github.com/klhq/skillmux/compare/v1.11.2...v1.12.0) (2026-09-12)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
* **manifest:** make the core skill cap configurable ([#190](https://github.com/klhq/skillmux/issues/190)) ([c5690b0](https://github.com/klhq/skillmux/commit/c5690b0f5fafda708ff87b991443e48bc02a4def))
|
|
14
|
+
|
|
8
15
|
## [1.11.2](https://github.com/klhq/skillmux/compare/v1.11.1...v1.11.2) (2026-09-05)
|
|
9
16
|
|
|
10
17
|
|
package/README.md
CHANGED
|
@@ -143,7 +143,8 @@ skillmux init \
|
|
|
143
143
|
--yes
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
Core pins apply to each configured target and stay capped at 25 skills
|
|
146
|
+
Core pins apply to each configured target and stay capped at 25 skills, or at
|
|
147
|
+
`[core].limit` when the manifest sets one. Add
|
|
147
148
|
project-specific skills from a repository root:
|
|
148
149
|
|
|
149
150
|
```sh
|
package/docs/cli.md
CHANGED
|
@@ -322,7 +322,7 @@ command prints the preserved path so cleanup remains an explicit user action.
|
|
|
322
322
|
## Core skills (`skillmux core`)
|
|
323
323
|
|
|
324
324
|
Pin or unpin skills into `[core]`, the tier every target receives by
|
|
325
|
-
default, capped at 25 skills:
|
|
325
|
+
default, capped at 25 skills unless `[core].limit` raises it:
|
|
326
326
|
|
|
327
327
|
```sh
|
|
328
328
|
skillmux core pin csv-formatter --yes
|
package/docs/concepts.md
CHANGED
|
@@ -55,7 +55,8 @@ one.
|
|
|
55
55
|
|
|
56
56
|
One skill can serve different roles across machines or projects, but the
|
|
57
57
|
shared manifest prevents conflicting core and project assignments. Core stays
|
|
58
|
-
capped at 25 skills to protect agent startup context.
|
|
58
|
+
capped at 25 skills by default to protect agent startup context. Raise or lower that with
|
|
59
|
+
`limit` under `[core]` in the manifest.
|
|
59
60
|
|
|
60
61
|
Delivery tiers do not select a deployment. A local Skillmux process can serve
|
|
61
62
|
routed skills over stdio, while a shared Skillmux process can serve its server
|
package/docs/configuration.md
CHANGED
|
@@ -284,7 +284,8 @@ Lives at the vault root (a legacy `skr.toml` is still read if present, never wri
|
|
|
284
284
|
|
|
285
285
|
```toml
|
|
286
286
|
[core]
|
|
287
|
-
skills = ["csv-formatter"] # pinned into every [targets.*] dir; capped at 25
|
|
287
|
+
skills = ["csv-formatter"] # pinned into every [targets.*] dir; capped at 25 by default
|
|
288
|
+
# limit = 30 # optional: raise or lower that cap
|
|
288
289
|
|
|
289
290
|
[project.repo1]
|
|
290
291
|
paths = ["/Users/you/code/repo1"] # only synced for paths that exist locally
|
|
@@ -295,12 +296,13 @@ host = "workhorse" # optional; init adds the current hostname
|
|
|
295
296
|
project_groups = ["repo1"] # which [project.*] groups materialize into this target; [] means none
|
|
296
297
|
```
|
|
297
298
|
|
|
298
|
-
- `[core].skills`: symlinked into every `[targets.*]` dir on `sync`. Capped at 25 skills; `sync` fails if a listed skill id isn't actually in the vault.
|
|
299
|
+
- `[core].skills`: symlinked into every `[targets.*]` dir on `sync`. Capped at 25 skills unless `[core].limit` says otherwise; `sync` fails if a listed skill id isn't actually in the vault.
|
|
300
|
+
- `[core].limit` (optional, positive integer): the cap on `[core].skills`. Absent means 25. It lives in the manifest rather than machine config so the same manifest validates identically on every machine.
|
|
299
301
|
- `[project.<group>].skills`: symlinked only into `<path>/<relative path from $HOME to the target dir>`, for each `paths` entry, and only for targets whose `project_groups` names that group. `paths` entries must resolve under `$HOME` (that's how the pin path is derived). A skill can't appear in both `[core]` and the same `[project.*]` group.
|
|
300
302
|
- `[project.<group>].paths` can list the same project's checkout on more than one machine (e.g. `["/home/alice/code/repo1", "/Users/alice/code/repo1"]`). `sync` silently skips any entry that doesn't exist on the machine it's running on (see below), so one shared manifest can span machines with different checkout locations without needing per-machine manifests.
|
|
301
303
|
- `[targets.<name>]`: one entry per adopted surface. Built-in names (`agent-skills`, `claude-code`, and `codex`) derive their directories from the name and omit `dir`. A custom target requires `dir`; create one with `skillmux target add <name> --dir <dir> --yes`. `skillmux target migrate --yes` removes legacy built-in `dir` fields without touching target files. An optional `host` limits the target to an exact machine-hostname match; omit it for a global, backward-compatible target. A host mismatch is reported and skipped before any target filesystem operation. `project_groups` is an explicit list, not a boolean: a target only receives the specific groups it names, never every group in the manifest.
|
|
302
304
|
|
|
303
|
-
**Pin/unpin without hand-editing.** `skillmux core pin`/`unpin` mutate `[core]` for you, and `skillmux project pin`/`unpin` mutate `[project.*]`, validating with the same rules `sync` enforces (skill must resolve from `vault_path`, no duplicate pins, `[core]` stays under
|
|
305
|
+
**Pin/unpin without hand-editing.** `skillmux core pin`/`unpin` mutate `[core]` for you, and `skillmux project pin`/`unpin` mutate `[project.*]`, validating with the same rules `sync` enforces (skill must resolve from `vault_path`, no duplicate pins, `[core]` stays under its cap) before writing anything:
|
|
304
306
|
|
|
305
307
|
```sh
|
|
306
308
|
skillmux core pin csv-formatter --yes # add to [core]
|
package/docs/skill-management.md
CHANGED
|
@@ -171,7 +171,7 @@ skillmux core unpin csv-formatter --yes
|
|
|
171
171
|
|
|
172
172
|
One command can change several skill IDs. Skillmux validates the complete
|
|
173
173
|
change before writing, so a conflict prevents the whole operation. Core stays
|
|
174
|
-
capped at 25 skills.
|
|
174
|
+
capped at 25 skills by default, which `[core].limit` overrides.
|
|
175
175
|
|
|
176
176
|
Run `skillmux sync` after a direct pin or unpin command.
|
|
177
177
|
|
package/package.json
CHANGED
package/src/init.ts
CHANGED
|
@@ -257,9 +257,10 @@ export function planInitManifest(
|
|
|
257
257
|
),
|
|
258
258
|
},
|
|
259
259
|
};
|
|
260
|
-
|
|
260
|
+
const effectiveLimit = manifest.core.limit ?? CORE_SKILL_LIMIT;
|
|
261
|
+
if (manifest.core.skills.length > effectiveLimit) {
|
|
261
262
|
throw new Error(
|
|
262
|
-
`[core] has ${manifest.core.skills.length} skills, exceeding the limit of ${
|
|
263
|
+
`[core] has ${manifest.core.skills.length} skills, exceeding the limit of ${effectiveLimit}`,
|
|
263
264
|
);
|
|
264
265
|
}
|
|
265
266
|
for (const skillId of coreSkillIds) {
|
package/src/manifest.ts
CHANGED
|
@@ -31,7 +31,10 @@ const targetSchema = z.object({
|
|
|
31
31
|
}).strict();
|
|
32
32
|
|
|
33
33
|
const manifestSchema = z.object({
|
|
34
|
-
core: z.object({
|
|
34
|
+
core: z.object({
|
|
35
|
+
skills: z.array(skillIdSchema),
|
|
36
|
+
limit: z.number().int().positive().optional(),
|
|
37
|
+
}).strict(),
|
|
35
38
|
project: z.record(groupNameSchema, projectGroupSchema).optional(),
|
|
36
39
|
targets: z.record(groupNameSchema, targetSchema).default({}),
|
|
37
40
|
}).strict();
|
|
@@ -98,7 +101,10 @@ function tomlStringArray(values: string[]): string {
|
|
|
98
101
|
|
|
99
102
|
/** Purpose-built serializer for this manifest's fixed shape — not a general TOML writer. */
|
|
100
103
|
export function serializeManifest(manifest: Manifest): string {
|
|
101
|
-
|
|
104
|
+
// An absent limit is left out rather than written as the default, so the file keeps saying
|
|
105
|
+
// "unset" and a future change to CORE_SKILL_LIMIT still reaches manifests that never opted in.
|
|
106
|
+
const coreLimit = manifest.core.limit === undefined ? "" : `\nlimit = ${manifest.core.limit}`;
|
|
107
|
+
const sections: string[] = [`[core]\nskills = ${tomlStringArray(manifest.core.skills)}${coreLimit}`];
|
|
102
108
|
|
|
103
109
|
for (const [name, group] of Object.entries(manifest.project ?? {})) {
|
|
104
110
|
sections.push(
|
|
@@ -140,14 +146,14 @@ export function pinCore(manifest: Manifest, skillId: string): Manifest {
|
|
|
140
146
|
if (existing) {
|
|
141
147
|
throw new Error(`skill "${skillId}" already pinned in ${existing}`);
|
|
142
148
|
}
|
|
143
|
-
return { ...manifest, core: { skills: [...manifest.core.skills, skillId] } };
|
|
149
|
+
return { ...manifest, core: { ...manifest.core, skills: [...manifest.core.skills, skillId] } };
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
export function unpinCore(manifest: Manifest, skillId: string): Manifest {
|
|
147
153
|
if (!manifest.core.skills.includes(skillId)) {
|
|
148
154
|
throw new Error(`skill "${skillId}" is not pinned in [core]`);
|
|
149
155
|
}
|
|
150
|
-
return { ...manifest, core: { skills: manifest.core.skills.filter((id) => id !== skillId) } };
|
|
156
|
+
return { ...manifest, core: { ...manifest.core, skills: manifest.core.skills.filter((id) => id !== skillId) } };
|
|
151
157
|
}
|
|
152
158
|
|
|
153
159
|
export function pinProject(manifest: Manifest, skillId: string, group: string, paths?: string[]): Manifest {
|
|
@@ -313,9 +319,10 @@ export function validateManifest(
|
|
|
313
319
|
vaultPath: string,
|
|
314
320
|
localVaultPaths: string[] = [],
|
|
315
321
|
): ManifestValidationResult {
|
|
316
|
-
|
|
322
|
+
const effectiveLimit = manifest.core.limit ?? CORE_SKILL_LIMIT;
|
|
323
|
+
if (manifest.core.skills.length > effectiveLimit) {
|
|
317
324
|
throw new Error(
|
|
318
|
-
`[core] has ${manifest.core.skills.length} skills, exceeding the limit of ${
|
|
325
|
+
`[core] has ${manifest.core.skills.length} skills, exceeding the limit of ${effectiveLimit}`,
|
|
319
326
|
);
|
|
320
327
|
}
|
|
321
328
|
|