@skillsmith/mcp-server 0.4.8 → 0.4.9
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 +11 -1
- package/README.md +3 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/tools/install.d.ts.map +1 -1
- package/dist/src/tools/install.js +9 -1
- package/dist/src/tools/install.js.map +1 -1
- package/dist/src/tools/publish.types.d.ts +1 -1
- package/dist/src/tools/skill-pack-audit.d.ts +22 -1
- package/dist/src/tools/skill-pack-audit.d.ts.map +1 -1
- package/dist/src/tools/skill-pack-audit.helpers.d.ts +53 -0
- package/dist/src/tools/skill-pack-audit.helpers.d.ts.map +1 -0
- package/dist/src/tools/skill-pack-audit.helpers.js +181 -0
- package/dist/src/tools/skill-pack-audit.helpers.js.map +1 -0
- package/dist/src/tools/skill-pack-audit.js +122 -5
- package/dist/src/tools/skill-pack-audit.js.map +1 -1
- package/dist/src/tools/skill-pack-audit.types.d.ts +70 -0
- package/dist/src/tools/skill-pack-audit.types.d.ts.map +1 -0
- package/dist/src/tools/skill-pack-audit.types.js +8 -0
- package/dist/src/tools/skill-pack-audit.types.js.map +1 -0
- package/dist/src/tools/validate.helpers.d.ts.map +1 -1
- package/dist/src/tools/validate.helpers.js +15 -0
- package/dist/src/tools/validate.helpers.js.map +1 -1
- package/dist/tests/unit/skill-pack-audit.test.js +225 -0
- package/dist/tests/unit/skill-pack-audit.test.js.map +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
package/dist/src/index.js
CHANGED
|
@@ -46,7 +46,7 @@ import { checkForUpdates, formatUpdateNotification } from '@skillsmith/core';
|
|
|
46
46
|
import { createLicenseMiddleware } from './middleware/license.js';
|
|
47
47
|
import { createQuotaMiddleware } from './middleware/quota.js';
|
|
48
48
|
// Package version - keep in sync with package.json
|
|
49
|
-
const PACKAGE_VERSION = '0.4.
|
|
49
|
+
const PACKAGE_VERSION = '0.4.9';
|
|
50
50
|
const PACKAGE_NAME = '@skillsmith/mcp-server';
|
|
51
51
|
import { installBundledSkills, installUserDocs, getUserGuidePath, } from './onboarding/install-assets.js';
|
|
52
52
|
// SMI-2679: Quota enforcement middleware — module-level singletons, initialized once
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/tools/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/tools/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAO1E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAG3C,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAc9F;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,YAAY,EACnB,QAAQ,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,aAAa,CAAC,CA4DxB"}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - Adds conflict resolution (three-way merge, backup) on top
|
|
14
14
|
* - Wires onProgress to MCP protocol notifications
|
|
15
15
|
*/
|
|
16
|
-
import { SkillInstallationService, } from '@skillsmith/core';
|
|
16
|
+
import { SkillInstallationService, emitInstallEvent, } from '@skillsmith/core';
|
|
17
17
|
import { getToolContext } from '../context.js';
|
|
18
18
|
import { loadManifest, lookupSkillFromRegistry } from './install.helpers.js';
|
|
19
19
|
// SMI-1867: Conflict resolution logic (extracted per governance review)
|
|
@@ -77,6 +77,7 @@ export async function installSkill(input, _context) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
// Delegate to core service
|
|
80
|
+
const installStart = Date.now();
|
|
80
81
|
const result = await service.install(input.skillId, {
|
|
81
82
|
force: input.force,
|
|
82
83
|
skipScan: input.skipScan,
|
|
@@ -84,6 +85,13 @@ export async function installSkill(input, _context) {
|
|
|
84
85
|
conflictAction: input.conflictAction,
|
|
85
86
|
confirmed: input.confirmed,
|
|
86
87
|
});
|
|
88
|
+
// SMI-4182: fire-and-forget install telemetry for usage report funnel
|
|
89
|
+
void emitInstallEvent({
|
|
90
|
+
skillId: input.skillId,
|
|
91
|
+
source: 'mcp',
|
|
92
|
+
success: result.success,
|
|
93
|
+
durationMs: Date.now() - installStart,
|
|
94
|
+
});
|
|
87
95
|
return result;
|
|
88
96
|
}
|
|
89
97
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/tools/install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,wBAAwB,
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/tools/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,8 +21,8 @@ export declare const publishInputSchema: z.ZodObject<{
|
|
|
21
21
|
/** Add claude-skill topic for registry discovery */
|
|
22
22
|
add_topic: z.ZodDefault<z.ZodBoolean>;
|
|
23
23
|
}, "strip", z.ZodTypeAny, {
|
|
24
|
-
visibility: "public" | "private";
|
|
25
24
|
skill_path: string;
|
|
25
|
+
visibility: "public" | "private";
|
|
26
26
|
check_references: boolean;
|
|
27
27
|
create_repo: boolean;
|
|
28
28
|
add_topic: boolean;
|
|
@@ -18,18 +18,22 @@
|
|
|
18
18
|
* Community users see a graceful license error response, never a hard throw.
|
|
19
19
|
*/
|
|
20
20
|
import { z } from 'zod';
|
|
21
|
+
import type { NamespaceFlag, TriggerQuality } from './skill-pack-audit.types.js';
|
|
21
22
|
import type { ToolContext } from '../context.js';
|
|
22
23
|
/**
|
|
23
24
|
* Input schema for skill_pack_audit tool
|
|
24
25
|
*/
|
|
25
26
|
export declare const skillPackAuditInputSchema: z.ZodObject<{
|
|
26
27
|
pack_path: z.ZodString;
|
|
28
|
+
check_trigger_quality: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
27
29
|
}, "strip", z.ZodTypeAny, {
|
|
28
30
|
pack_path: string;
|
|
31
|
+
check_trigger_quality: boolean;
|
|
29
32
|
}, {
|
|
30
33
|
pack_path: string;
|
|
34
|
+
check_trigger_quality?: boolean | undefined;
|
|
31
35
|
}>;
|
|
32
|
-
export type SkillPackAuditInput = z.
|
|
36
|
+
export type SkillPackAuditInput = z.input<typeof skillPackAuditInputSchema>;
|
|
33
37
|
/**
|
|
34
38
|
* Drift status for a single skill in the pack
|
|
35
39
|
*/
|
|
@@ -63,6 +67,18 @@ export interface SkillPackAuditResponse {
|
|
|
63
67
|
noRegistryDataCount: number;
|
|
64
68
|
/** Per-skill audit results, sorted alphabetically by name */
|
|
65
69
|
skills: PackSkillEntry[];
|
|
70
|
+
/**
|
|
71
|
+
* SMI-4124: Trigger-quality analysis across the pack (generic trigger words
|
|
72
|
+
* in skill names/descriptions). Present when `check_trigger_quality` is `true`
|
|
73
|
+
* (default). Omitted when the caller explicitly opts out.
|
|
74
|
+
*/
|
|
75
|
+
triggerQuality?: TriggerQuality;
|
|
76
|
+
/**
|
|
77
|
+
* SMI-4124: Namespace-quality flag on the pack itself. Present (possibly
|
|
78
|
+
* `null`) when `check_trigger_quality` is `true`. `null` = clean pack name.
|
|
79
|
+
* Omitted when the caller opts out.
|
|
80
|
+
*/
|
|
81
|
+
namespaceQuality?: NamespaceFlag | null;
|
|
66
82
|
}
|
|
67
83
|
/**
|
|
68
84
|
* MCP tool definition for skill_pack_audit
|
|
@@ -77,6 +93,11 @@ export declare const skillPackAuditToolSchema: {
|
|
|
77
93
|
type: string;
|
|
78
94
|
description: string;
|
|
79
95
|
};
|
|
96
|
+
check_trigger_quality: {
|
|
97
|
+
type: string;
|
|
98
|
+
default: boolean;
|
|
99
|
+
description: string;
|
|
100
|
+
};
|
|
80
101
|
};
|
|
81
102
|
required: string[];
|
|
82
103
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-pack-audit.d.ts","sourceRoot":"","sources":["../../../src/tools/skill-pack-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-pack-audit.d.ts","sourceRoot":"","sources":["../../../src/tools/skill-pack-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAUvB,OAAO,KAAK,EAEV,aAAa,EACb,cAAc,EAEf,MAAM,6BAA6B,CAAA;AACpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAMhD;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;EAiBpC,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAE3E;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,UAAU,GACV,OAAO,GACP,kBAAkB,GAClB,iBAAiB,CAAA;AAErB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,gFAAgF;IAChF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,mBAAmB;IACnB,MAAM,EAAE,eAAe,CAAA;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAA;IAClB,sFAAsF;IACtF,UAAU,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,mBAAmB,EAAE,MAAM,CAAA;IAC3B,6DAA6D;IAC7D,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;CACxC;AAMD;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;CA6BpC,CAAA;AA4BD;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,sBAAsB,CAAC,CAkNjC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Generic trigger-word + namespace detection helpers for
|
|
3
|
+
* `skill_pack_audit`.
|
|
4
|
+
* @module @skillsmith/mcp-server/tools/skill-pack-audit.helpers
|
|
5
|
+
* @see SMI-4124
|
|
6
|
+
*
|
|
7
|
+
* Pure functions — no I/O, no database access. Tested via
|
|
8
|
+
* `tests/unit/skill-pack-audit.test.ts`.
|
|
9
|
+
*/
|
|
10
|
+
import type { GenericTriggersStoplist } from '@skillsmith/core';
|
|
11
|
+
import type { GenericWordFlag, NamespaceFlag } from './skill-pack-audit.types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Detect generic trigger words in a skill's name and description.
|
|
14
|
+
*
|
|
15
|
+
* - Skill-name hits produce `severity: 'error'` (unconditional false-trigger magnet).
|
|
16
|
+
* - Description hits produce `severity: 'warning'`.
|
|
17
|
+
* - `suggested` is `${packDomain}-${token}` when a pack domain is available,
|
|
18
|
+
* otherwise `null`.
|
|
19
|
+
*
|
|
20
|
+
* @param description - Raw description value from frontmatter (may be string
|
|
21
|
+
* or string[] from block-scalar parsing, or other).
|
|
22
|
+
* @param skillName - Skill's `name` frontmatter value (falls back to dir name).
|
|
23
|
+
* @param packDomain - Inferred pack domain, or `null` when indeterminate.
|
|
24
|
+
* @param stoplist - Curated stoplist from `@skillsmith/core`.
|
|
25
|
+
* @returns Flags (name errors first, then description warnings).
|
|
26
|
+
*/
|
|
27
|
+
export declare function detectGenericTriggerWords(description: unknown, skillName: string, packDomain: string | null, stoplist: GenericTriggersStoplist): GenericWordFlag[];
|
|
28
|
+
/**
|
|
29
|
+
* Aggregate per-skill `tags` across a pack to derive a pack domain.
|
|
30
|
+
*
|
|
31
|
+
* Priority:
|
|
32
|
+
* 1. If `packName` ends with `-skills` and is not itself generic, strip the
|
|
33
|
+
* suffix and return the prefix (e.g. `planning-skills` → `planning`).
|
|
34
|
+
* 2. Otherwise, compute the mode of non-generic tags across all skills.
|
|
35
|
+
* 3. Returns `null` when no domain can be inferred with confidence.
|
|
36
|
+
*
|
|
37
|
+
* @param packName - Pack directory name.
|
|
38
|
+
* @param allSkills - Per-skill tag arrays (undefined / non-array → ignored).
|
|
39
|
+
* @param stoplist - Curated stoplist (for generic-tag filtering).
|
|
40
|
+
*/
|
|
41
|
+
export declare function derivePackDomain(packName: string, allSkills: Array<{
|
|
42
|
+
tags?: unknown;
|
|
43
|
+
}>, stoplist: GenericTriggersStoplist): string | null;
|
|
44
|
+
/**
|
|
45
|
+
* Detect a generic pack namespace.
|
|
46
|
+
*
|
|
47
|
+
* @returns NamespaceFlag when the pack name matches a generic namespace,
|
|
48
|
+
* otherwise null.
|
|
49
|
+
*/
|
|
50
|
+
export declare function detectGenericNamespace(packName: string, allSkills: Array<{
|
|
51
|
+
tags?: unknown;
|
|
52
|
+
}>, stoplist: GenericTriggersStoplist): NamespaceFlag | null;
|
|
53
|
+
//# sourceMappingURL=skill-pack-audit.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-pack-audit.helpers.d.ts","sourceRoot":"","sources":["../../../src/tools/skill-pack-audit.helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAE/D,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AA4BjF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,EAAE,uBAAuB,GAChC,eAAe,EAAE,CAqDnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACpC,QAAQ,EAAE,uBAAuB,GAChC,MAAM,GAAG,IAAI,CAyCf;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACpC,QAAQ,EAAE,uBAAuB,GAChC,aAAa,GAAG,IAAI,CAqBtB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Generic trigger-word + namespace detection helpers for
|
|
3
|
+
* `skill_pack_audit`.
|
|
4
|
+
* @module @skillsmith/mcp-server/tools/skill-pack-audit.helpers
|
|
5
|
+
* @see SMI-4124
|
|
6
|
+
*
|
|
7
|
+
* Pure functions — no I/O, no database access. Tested via
|
|
8
|
+
* `tests/unit/skill-pack-audit.test.ts`.
|
|
9
|
+
*/
|
|
10
|
+
import { FIELD_LIMITS } from './validate.types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Coerce a description value into a plain string. `parseYamlFrontmatter` returns
|
|
13
|
+
* block-scalar (`description: |`) values as `string[]`, so we join with spaces.
|
|
14
|
+
* Non-string/non-string-array values yield an empty string.
|
|
15
|
+
*/
|
|
16
|
+
function coerceDescription(description) {
|
|
17
|
+
if (typeof description === 'string')
|
|
18
|
+
return description;
|
|
19
|
+
if (Array.isArray(description)) {
|
|
20
|
+
return description.filter((item) => typeof item === 'string').join(' ');
|
|
21
|
+
}
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
/** Split cleaned description text into lowercase word tokens. */
|
|
25
|
+
function tokenizeForTriggers(text) {
|
|
26
|
+
// Clamp to the same limit `validate` enforces to prevent quadratic scans
|
|
27
|
+
// on malicious oversized frontmatter.
|
|
28
|
+
const clamped = text.slice(0, FIELD_LIMITS.description);
|
|
29
|
+
// Strip common punctuation / markdown, collapse whitespace.
|
|
30
|
+
return clamped
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
33
|
+
.split(/\s+/)
|
|
34
|
+
.filter((t) => t.length > 0);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Detect generic trigger words in a skill's name and description.
|
|
38
|
+
*
|
|
39
|
+
* - Skill-name hits produce `severity: 'error'` (unconditional false-trigger magnet).
|
|
40
|
+
* - Description hits produce `severity: 'warning'`.
|
|
41
|
+
* - `suggested` is `${packDomain}-${token}` when a pack domain is available,
|
|
42
|
+
* otherwise `null`.
|
|
43
|
+
*
|
|
44
|
+
* @param description - Raw description value from frontmatter (may be string
|
|
45
|
+
* or string[] from block-scalar parsing, or other).
|
|
46
|
+
* @param skillName - Skill's `name` frontmatter value (falls back to dir name).
|
|
47
|
+
* @param packDomain - Inferred pack domain, or `null` when indeterminate.
|
|
48
|
+
* @param stoplist - Curated stoplist from `@skillsmith/core`.
|
|
49
|
+
* @returns Flags (name errors first, then description warnings).
|
|
50
|
+
*/
|
|
51
|
+
export function detectGenericTriggerWords(description, skillName, packDomain, stoplist) {
|
|
52
|
+
const flags = [];
|
|
53
|
+
const triggerSet = new Set(stoplist.triggerWords.map((w) => w.toLowerCase()));
|
|
54
|
+
// Name check (error): tokenize on non-alphanumerics so "spec-builder" flags both.
|
|
55
|
+
const nameTokens = new Set(skillName
|
|
56
|
+
.toLowerCase()
|
|
57
|
+
.split(/[^a-z0-9]+/)
|
|
58
|
+
.filter((t) => t.length > 0));
|
|
59
|
+
for (const token of nameTokens) {
|
|
60
|
+
if (triggerSet.has(token)) {
|
|
61
|
+
flags.push({
|
|
62
|
+
token,
|
|
63
|
+
location: 'name',
|
|
64
|
+
severity: 'error',
|
|
65
|
+
suggested: packDomain ? `${packDomain}-${token}` : null,
|
|
66
|
+
reason: `Skill name contains generic trigger word "${token}". ` +
|
|
67
|
+
`This causes Claude's skill-trigger heuristic to misfire on unrelated prompts. ` +
|
|
68
|
+
(packDomain
|
|
69
|
+
? `Rename to "${packDomain}-${token}" to qualify the scope.`
|
|
70
|
+
: `Qualify with a domain prefix (e.g. "planning-${token}").`),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Description check (warning): full-text tokenization.
|
|
75
|
+
const descText = coerceDescription(description);
|
|
76
|
+
if (descText.length > 0) {
|
|
77
|
+
const descTokens = tokenizeForTriggers(descText);
|
|
78
|
+
const seen = new Set();
|
|
79
|
+
for (const token of descTokens) {
|
|
80
|
+
if (seen.has(token))
|
|
81
|
+
continue;
|
|
82
|
+
if (nameTokens.has(token))
|
|
83
|
+
continue; // already flagged as name error
|
|
84
|
+
if (triggerSet.has(token)) {
|
|
85
|
+
seen.add(token);
|
|
86
|
+
flags.push({
|
|
87
|
+
token,
|
|
88
|
+
location: 'description',
|
|
89
|
+
severity: 'warning',
|
|
90
|
+
suggested: packDomain ? `${packDomain}-${token}` : null,
|
|
91
|
+
reason: `Description contains generic trigger word "${token}". ` +
|
|
92
|
+
`Consider rewording or qualifying the skill's activation phrasing to ` +
|
|
93
|
+
`avoid false-positive skill triggers.`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return flags;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Aggregate per-skill `tags` across a pack to derive a pack domain.
|
|
102
|
+
*
|
|
103
|
+
* Priority:
|
|
104
|
+
* 1. If `packName` ends with `-skills` and is not itself generic, strip the
|
|
105
|
+
* suffix and return the prefix (e.g. `planning-skills` → `planning`).
|
|
106
|
+
* 2. Otherwise, compute the mode of non-generic tags across all skills.
|
|
107
|
+
* 3. Returns `null` when no domain can be inferred with confidence.
|
|
108
|
+
*
|
|
109
|
+
* @param packName - Pack directory name.
|
|
110
|
+
* @param allSkills - Per-skill tag arrays (undefined / non-array → ignored).
|
|
111
|
+
* @param stoplist - Curated stoplist (for generic-tag filtering).
|
|
112
|
+
*/
|
|
113
|
+
export function derivePackDomain(packName, allSkills, stoplist) {
|
|
114
|
+
const genericNamespaces = new Set(stoplist.namespaces.map((n) => n.toLowerCase()));
|
|
115
|
+
const genericWords = new Set(stoplist.triggerWords.map((w) => w.toLowerCase()));
|
|
116
|
+
// Strategy 1: strip `-skills` suffix from non-generic pack name.
|
|
117
|
+
const lowerPack = packName.toLowerCase();
|
|
118
|
+
if (lowerPack.endsWith('-skills') && !genericNamespaces.has(lowerPack)) {
|
|
119
|
+
const prefix = lowerPack.slice(0, -'-skills'.length);
|
|
120
|
+
if (prefix.length > 0 && !genericNamespaces.has(prefix) && !genericWords.has(prefix)) {
|
|
121
|
+
return prefix;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Strategy 2: mode of per-skill tags.
|
|
125
|
+
const counts = new Map();
|
|
126
|
+
for (const skill of allSkills) {
|
|
127
|
+
if (!Array.isArray(skill.tags))
|
|
128
|
+
continue;
|
|
129
|
+
for (const raw of skill.tags) {
|
|
130
|
+
if (typeof raw !== 'string')
|
|
131
|
+
continue;
|
|
132
|
+
const tag = raw.toLowerCase().trim();
|
|
133
|
+
if (tag.length === 0)
|
|
134
|
+
continue;
|
|
135
|
+
if (genericNamespaces.has(tag) || genericWords.has(tag))
|
|
136
|
+
continue;
|
|
137
|
+
counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (counts.size === 0)
|
|
141
|
+
return null;
|
|
142
|
+
let bestTag = null;
|
|
143
|
+
let bestCount = 0;
|
|
144
|
+
for (const [tag, count] of counts) {
|
|
145
|
+
if (count > bestCount) {
|
|
146
|
+
bestTag = tag;
|
|
147
|
+
bestCount = count;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Require the mode to cover at least 2 skills (or the only skill) to avoid
|
|
151
|
+
// picking random one-offs.
|
|
152
|
+
const minSkills = allSkills.length >= 2 ? 2 : 1;
|
|
153
|
+
return bestCount >= minSkills ? bestTag : null;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Detect a generic pack namespace.
|
|
157
|
+
*
|
|
158
|
+
* @returns NamespaceFlag when the pack name matches a generic namespace,
|
|
159
|
+
* otherwise null.
|
|
160
|
+
*/
|
|
161
|
+
export function detectGenericNamespace(packName, allSkills, stoplist) {
|
|
162
|
+
const lowerPack = packName.toLowerCase();
|
|
163
|
+
const genericNamespaces = new Set(stoplist.namespaces.map((n) => n.toLowerCase()));
|
|
164
|
+
if (!genericNamespaces.has(lowerPack))
|
|
165
|
+
return null;
|
|
166
|
+
const domain = derivePackDomain(packName, allSkills, stoplist);
|
|
167
|
+
const suggested = domain ? `${domain}-skills` : null;
|
|
168
|
+
const reason = suggested
|
|
169
|
+
? `Pack name "${packName}" is a generic namespace. Rename to "${suggested}" ` +
|
|
170
|
+
`to reflect its domain (inferred from per-skill tags).`
|
|
171
|
+
: `Pack name "${packName}" is a generic namespace and per-skill tags do not ` +
|
|
172
|
+
`converge on a clear domain. Rename to "<domain>-skills" where <domain> is ` +
|
|
173
|
+
`a specific scope (e.g. "planning-skills", "cicd-skills").`;
|
|
174
|
+
return {
|
|
175
|
+
packName,
|
|
176
|
+
severity: 'warning',
|
|
177
|
+
suggested,
|
|
178
|
+
reason,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=skill-pack-audit.helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-pack-audit.helpers.js","sourceRoot":"","sources":["../../../src/tools/skill-pack-audit.helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { z } from 'zod';
|
|
21
21
|
import { promises as fs } from 'fs';
|
|
22
|
-
import { join, resolve, sep } from 'path';
|
|
23
|
-
import { SkillsmithError, ErrorCodes } from '@skillsmith/core';
|
|
22
|
+
import { basename, join, resolve, sep } from 'path';
|
|
23
|
+
import { SkillsmithError, ErrorCodes, GENERIC_TRIGGERS } from '@skillsmith/core';
|
|
24
24
|
import { parseYamlFrontmatter, hasPathTraversal } from './validate.helpers.js';
|
|
25
|
+
import { detectGenericTriggerWords, detectGenericNamespace, derivePackDomain, } from './skill-pack-audit.helpers.js';
|
|
25
26
|
// ============================================================================
|
|
26
27
|
// Input / Output types
|
|
27
28
|
// ============================================================================
|
|
@@ -34,6 +35,13 @@ export const skillPackAuditInputSchema = z.object({
|
|
|
34
35
|
.min(1)
|
|
35
36
|
.describe('Absolute path to the skill pack root directory. ' +
|
|
36
37
|
'Must contain a skills/ subdirectory with skill folders each containing SKILL.md.'),
|
|
38
|
+
check_trigger_quality: z
|
|
39
|
+
.boolean()
|
|
40
|
+
.optional()
|
|
41
|
+
.default(true)
|
|
42
|
+
.describe('When true (default), also flag generic trigger words in skill name/description ' +
|
|
43
|
+
'and generic pack namespaces with rename suggestions. Set false for ' +
|
|
44
|
+
'version-drift-only audits (legacy response shape). SMI-4124.'),
|
|
37
45
|
});
|
|
38
46
|
// ============================================================================
|
|
39
47
|
// Tool schema (MCP tool definition)
|
|
@@ -43,9 +51,12 @@ export const skillPackAuditInputSchema = z.object({
|
|
|
43
51
|
*/
|
|
44
52
|
export const skillPackAuditToolSchema = {
|
|
45
53
|
name: 'skill_pack_audit',
|
|
46
|
-
description: 'Audit a skill pack directory for version drift
|
|
47
|
-
'
|
|
48
|
-
'
|
|
54
|
+
description: 'Audit a skill pack directory for (a) version drift — bundled SKILL.md versions vs. ' +
|
|
55
|
+
'the Skillsmith registry cache — and (b) trigger-quality issues — generic trigger ' +
|
|
56
|
+
'words in skill names/descriptions and generic pack namespaces that misfire ' +
|
|
57
|
+
"Claude's skill-trigger heuristic (SMI-4124). Response is additive: new " +
|
|
58
|
+
'triggerQuality and namespaceQuality fields appear when check_trigger_quality is ' +
|
|
59
|
+
'true (default); existing fields (skills, driftCount, etc.) are unchanged. ' +
|
|
49
60
|
'Requires Individual tier or higher.',
|
|
50
61
|
inputSchema: {
|
|
51
62
|
type: 'object',
|
|
@@ -54,6 +65,13 @@ export const skillPackAuditToolSchema = {
|
|
|
54
65
|
type: 'string',
|
|
55
66
|
description: 'Absolute path to the skill pack root directory (must contain a skills/ subdirectory).',
|
|
56
67
|
},
|
|
68
|
+
check_trigger_quality: {
|
|
69
|
+
type: 'boolean',
|
|
70
|
+
default: true,
|
|
71
|
+
description: 'When true (default), also flag generic trigger words and generic pack ' +
|
|
72
|
+
'namespaces with rename suggestions. Set false for version-drift-only audits ' +
|
|
73
|
+
'(legacy response shape — triggerQuality and namespaceQuality fields omitted).',
|
|
74
|
+
},
|
|
57
75
|
},
|
|
58
76
|
required: ['pack_path'],
|
|
59
77
|
},
|
|
@@ -100,6 +118,8 @@ export async function executeSkillPackAudit(input, context) {
|
|
|
100
118
|
}
|
|
101
119
|
const packPath = resolve(input.pack_path);
|
|
102
120
|
const skillsDir = join(packPath, 'skills');
|
|
121
|
+
const packName = basename(packPath);
|
|
122
|
+
const checkTriggerQuality = input.check_trigger_quality !== false;
|
|
103
123
|
// Discover subdirectories in skills/
|
|
104
124
|
let skillDirNames;
|
|
105
125
|
try {
|
|
@@ -113,6 +133,10 @@ export async function executeSkillPackAudit(input, context) {
|
|
|
113
133
|
throw new SkillsmithError(ErrorCodes.VALIDATION_INVALID_TYPE, `Pack contains ${skillDirNames.length} skill directories; maximum is 500`);
|
|
114
134
|
}
|
|
115
135
|
const skills = [];
|
|
136
|
+
// SMI-4124: parallel accumulators for trigger-quality analysis.
|
|
137
|
+
// We keep these outside PackSkillEntry to preserve the legacy response shape
|
|
138
|
+
// for callers who opt out of trigger-quality via check_trigger_quality: false.
|
|
139
|
+
const skillMeta = [];
|
|
116
140
|
for (const dirName of skillDirNames) {
|
|
117
141
|
const skillMdPath = join(skillsDir, dirName, 'SKILL.md');
|
|
118
142
|
let resolvedMdPath;
|
|
@@ -168,16 +192,109 @@ export async function executeSkillPackAudit(input, context) {
|
|
|
168
192
|
status = 'ahead';
|
|
169
193
|
}
|
|
170
194
|
skills.push({ name, bundledVersion, registryVersion, skillId, status });
|
|
195
|
+
// Capture description/tags for SMI-4124 trigger-quality analysis. We read
|
|
196
|
+
// these out of metadata (already parsed) regardless of the flag, since the
|
|
197
|
+
// cost is trivial; the analysis itself is gated below.
|
|
198
|
+
skillMeta.push({
|
|
199
|
+
name,
|
|
200
|
+
description: metadata?.description,
|
|
201
|
+
tags: metadata?.tags,
|
|
202
|
+
});
|
|
171
203
|
}
|
|
172
204
|
skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
173
205
|
const driftCount = skills.filter((s) => s.status === 'outdated' || s.status === 'ahead').length;
|
|
174
206
|
const noRegistryDataCount = skills.filter((s) => s.status === 'no_registry_data').length;
|
|
207
|
+
// SMI-4124: Trigger-quality + namespace analysis (additive response growth).
|
|
208
|
+
// When the caller opts out, we omit both fields so the response shape matches
|
|
209
|
+
// the pre-extension contract byte-for-byte.
|
|
210
|
+
if (!checkTriggerQuality) {
|
|
211
|
+
return {
|
|
212
|
+
packPath,
|
|
213
|
+
skillCount: skills.length,
|
|
214
|
+
driftCount,
|
|
215
|
+
noRegistryDataCount,
|
|
216
|
+
skills,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
const stoplist = GENERIC_TRIGGERS;
|
|
220
|
+
const packDomain = derivePackDomain(packName, skillMeta, stoplist);
|
|
221
|
+
// Per-skill flags (sorted alphabetically by skill name to match `skills`).
|
|
222
|
+
const sortedMeta = [...skillMeta].sort((a, b) => a.name.localeCompare(b.name));
|
|
223
|
+
const triggerEntries = [];
|
|
224
|
+
const nameFlagsByToken = new Map();
|
|
225
|
+
for (const meta of sortedMeta) {
|
|
226
|
+
const flags = detectGenericTriggerWords(meta.description, meta.name, packDomain, stoplist);
|
|
227
|
+
if (flags.length > 0) {
|
|
228
|
+
triggerEntries.push({ id: meta.name, flags });
|
|
229
|
+
// Track name-level flags so we can dedup against a namespace hit below.
|
|
230
|
+
for (const flag of flags) {
|
|
231
|
+
if (flag.location === 'name') {
|
|
232
|
+
const list = nameFlagsByToken.get(flag.token) ?? [];
|
|
233
|
+
list.push(flag);
|
|
234
|
+
nameFlagsByToken.set(flag.token, list);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// Namespace flag + dedup: if the pack name is generic AND a skill-name flag
|
|
240
|
+
// exists for the same root token, drop the duplicate skill-name error and
|
|
241
|
+
// fold its reason into the namespace warning's reason.
|
|
242
|
+
let namespaceFlag = detectGenericNamespace(packName, skillMeta, stoplist);
|
|
243
|
+
if (namespaceFlag) {
|
|
244
|
+
const packToken = packName.toLowerCase();
|
|
245
|
+
// Check if any skill-name flag shares a token with the pack name. Typical
|
|
246
|
+
// case: pack "skills" and a skill named "skills" — not common — but also
|
|
247
|
+
// covers partial-token overlap like pack "tools" and skill "build-tools"
|
|
248
|
+
// (namespace covers the generic concern; skill-name flag is redundant).
|
|
249
|
+
const overlappingTokens = [];
|
|
250
|
+
for (const [token] of nameFlagsByToken) {
|
|
251
|
+
if (packToken === token || packToken.includes(token) || token.includes(packToken)) {
|
|
252
|
+
overlappingTokens.push(token);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (overlappingTokens.length > 0) {
|
|
256
|
+
// Remove the overlapping name flags from triggerEntries.
|
|
257
|
+
for (let i = triggerEntries.length - 1; i >= 0; i--) {
|
|
258
|
+
const entry = triggerEntries[i];
|
|
259
|
+
entry.flags = entry.flags.filter((f) => !(f.location === 'name' && overlappingTokens.includes(f.token)));
|
|
260
|
+
if (entry.flags.length === 0) {
|
|
261
|
+
triggerEntries.splice(i, 1);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Merge the removed-flag reasoning into the namespace reason.
|
|
265
|
+
namespaceFlag = {
|
|
266
|
+
...namespaceFlag,
|
|
267
|
+
reason: namespaceFlag.reason +
|
|
268
|
+
` Also applies to skill(s) whose name contains the same generic token(s): ` +
|
|
269
|
+
overlappingTokens.map((t) => `"${t}"`).join(', ') +
|
|
270
|
+
` — resolved by renaming the pack namespace.`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
let totalFlags = 0;
|
|
275
|
+
let errorCount = 0;
|
|
276
|
+
let warningCount = 0;
|
|
277
|
+
for (const entry of triggerEntries) {
|
|
278
|
+
for (const flag of entry.flags) {
|
|
279
|
+
totalFlags++;
|
|
280
|
+
if (flag.severity === 'error')
|
|
281
|
+
errorCount++;
|
|
282
|
+
else
|
|
283
|
+
warningCount++;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const triggerQuality = {
|
|
287
|
+
skills: triggerEntries,
|
|
288
|
+
summary: { totalFlags, errorCount, warningCount },
|
|
289
|
+
};
|
|
175
290
|
return {
|
|
176
291
|
packPath,
|
|
177
292
|
skillCount: skills.length,
|
|
178
293
|
driftCount,
|
|
179
294
|
noRegistryDataCount,
|
|
180
295
|
skills,
|
|
296
|
+
triggerQuality,
|
|
297
|
+
namespaceQuality: namespaceFlag,
|
|
181
298
|
};
|
|
182
299
|
}
|
|
183
300
|
//# sourceMappingURL=skill-pack-audit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-pack-audit.js","sourceRoot":"","sources":["../../../src/tools/skill-pack-audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"skill-pack-audit.js","sourceRoot":"","sources":["../../../src/tools/skill-pack-audit.ts"],"names":[],"mappings":"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"}
|