@skillsmith/core 0.12.1 → 0.12.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 +155 -0
- package/README.md +2 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/api/client.d.ts +5 -3
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +23 -33
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/api/client.registry-sync.d.ts +28 -0
- package/dist/src/api/client.registry-sync.d.ts.map +1 -0
- package/dist/src/api/client.registry-sync.js +36 -0
- package/dist/src/api/client.registry-sync.js.map +1 -0
- package/dist/src/api/client.test.js +185 -0
- package/dist/src/api/client.test.js.map +1 -1
- package/dist/src/api/client.toSkill.d.ts +16 -0
- package/dist/src/api/client.toSkill.d.ts.map +1 -0
- package/dist/src/api/client.toSkill.js +46 -0
- package/dist/src/api/client.toSkill.js.map +1 -0
- package/dist/src/api/client.types.d.ts +22 -0
- package/dist/src/api/client.types.d.ts.map +1 -1
- package/dist/src/api/index.d.ts +1 -1
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/schemas.d.ts +28 -0
- package/dist/src/api/schemas.d.ts.map +1 -1
- package/dist/src/api/schemas.js +13 -0
- package/dist/src/api/schemas.js.map +1 -1
- package/dist/src/audit/remote-audit.d.ts +85 -0
- package/dist/src/audit/remote-audit.d.ts.map +1 -1
- package/dist/src/audit/remote-audit.js +126 -3
- package/dist/src/audit/remote-audit.js.map +1 -1
- package/dist/src/audit/remote-audit.test.js +217 -1
- package/dist/src/audit/remote-audit.test.js.map +1 -1
- package/dist/src/config/index.d.ts +1 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/db/migration-runner.d.ts.map +1 -1
- package/dist/src/db/migration-runner.js +6 -0
- package/dist/src/db/migration-runner.js.map +1 -1
- package/dist/src/db/migrations/v18-skill-versions-real-content-hash.d.ts +36 -0
- package/dist/src/db/migrations/v18-skill-versions-real-content-hash.d.ts.map +1 -0
- package/dist/src/db/migrations/v18-skill-versions-real-content-hash.js +38 -0
- package/dist/src/db/migrations/v18-skill-versions-real-content-hash.js.map +1 -0
- package/dist/src/db/schema.d.ts +1 -1
- package/dist/src/db/schema.d.ts.map +1 -1
- package/dist/src/db/schema.js +3 -1
- package/dist/src/db/schema.js.map +1 -1
- package/dist/src/exports/services.d.ts +3 -7
- package/dist/src/exports/services.d.ts.map +1 -1
- package/dist/src/exports/services.install.d.ts +18 -0
- package/dist/src/exports/services.install.d.ts.map +1 -0
- package/dist/src/exports/services.install.js +62 -0
- package/dist/src/exports/services.install.js.map +1 -0
- package/dist/src/exports/services.js +12 -32
- package/dist/src/exports/services.js.map +1 -1
- package/dist/src/exports/types.d.ts +1 -1
- package/dist/src/exports/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +5 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/install/agent-config-merge.json.d.ts +5 -4
- package/dist/src/install/agent-config-merge.json.d.ts.map +1 -1
- package/dist/src/install/agent-config-merge.json.js +10 -9
- package/dist/src/install/agent-config-merge.json.js.map +1 -1
- package/dist/src/install/agent-config-merge.types.d.ts +11 -1
- package/dist/src/install/agent-config-merge.types.d.ts.map +1 -1
- package/dist/src/install/agent-config-merge.types.js.map +1 -1
- package/dist/src/install/agent-harness-targets.d.ts +30 -2
- package/dist/src/install/agent-harness-targets.d.ts.map +1 -1
- package/dist/src/install/agent-harness-targets.js +17 -0
- package/dist/src/install/agent-harness-targets.js.map +1 -1
- package/dist/src/install/agent-manifest-path-guard.d.ts +28 -3
- package/dist/src/install/agent-manifest-path-guard.d.ts.map +1 -1
- package/dist/src/install/agent-manifest-path-guard.js +59 -3
- package/dist/src/install/agent-manifest-path-guard.js.map +1 -1
- package/dist/src/install/agent-manifest-path-guard.test.js +27 -1
- package/dist/src/install/agent-manifest-path-guard.test.js.map +1 -1
- package/dist/src/install/agent-pack-installer.antigravity-mcp.d.ts +74 -0
- package/dist/src/install/agent-pack-installer.antigravity-mcp.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.antigravity-mcp.js +106 -0
- package/dist/src/install/agent-pack-installer.antigravity-mcp.js.map +1 -0
- package/dist/src/install/agent-pack-installer.antigravity-mcp.test.d.ts +2 -0
- package/dist/src/install/agent-pack-installer.antigravity-mcp.test.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.antigravity-mcp.test.js +157 -0
- package/dist/src/install/agent-pack-installer.antigravity-mcp.test.js.map +1 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.d.ts +53 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.js +207 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.js.map +1 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.test.d.ts +2 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.test.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.test.js +285 -0
- package/dist/src/install/agent-pack-installer.cursor-mcp.test.js.map +1 -0
- package/dist/src/install/agent-pack-installer.d.ts +8 -0
- package/dist/src/install/agent-pack-installer.d.ts.map +1 -1
- package/dist/src/install/agent-pack-installer.entry.d.ts +120 -3
- package/dist/src/install/agent-pack-installer.entry.d.ts.map +1 -1
- package/dist/src/install/agent-pack-installer.entry.js +166 -3
- package/dist/src/install/agent-pack-installer.entry.js.map +1 -1
- package/dist/src/install/agent-pack-installer.js +97 -3
- package/dist/src/install/agent-pack-installer.js.map +1 -1
- package/dist/src/install/agent-pack-installer.types.d.ts +27 -0
- package/dist/src/install/agent-pack-installer.types.d.ts.map +1 -1
- package/dist/src/install/agent-pack-installer.types.js +6 -0
- package/dist/src/install/agent-pack-installer.types.js.map +1 -1
- package/dist/src/install/agent-pack-installer.workspace-scope.test.d.ts +2 -0
- package/dist/src/install/agent-pack-installer.workspace-scope.test.d.ts.map +1 -0
- package/dist/src/install/agent-pack-installer.workspace-scope.test.js +148 -0
- package/dist/src/install/agent-pack-installer.workspace-scope.test.js.map +1 -0
- package/dist/src/install/agent-pack-uninstaller.d.ts +18 -7
- package/dist/src/install/agent-pack-uninstaller.d.ts.map +1 -1
- package/dist/src/install/agent-pack-uninstaller.js +59 -9
- package/dist/src/install/agent-pack-uninstaller.js.map +1 -1
- package/dist/src/install/agent-pack-uninstaller.test.js +74 -1
- package/dist/src/install/agent-pack-uninstaller.test.js.map +1 -1
- package/dist/src/install/fan-out.d.ts.map +1 -1
- package/dist/src/install/fan-out.js +6 -0
- package/dist/src/install/fan-out.js.map +1 -1
- package/dist/src/install/index.d.ts +2 -0
- package/dist/src/install/index.d.ts.map +1 -1
- package/dist/src/install/index.js +2 -0
- package/dist/src/install/index.js.map +1 -1
- package/dist/src/install/workspace-scope.d.ts +207 -0
- package/dist/src/install/workspace-scope.d.ts.map +1 -0
- package/dist/src/install/workspace-scope.js +275 -0
- package/dist/src/install/workspace-scope.js.map +1 -0
- package/dist/src/install/workspace-scope.test.d.ts +2 -0
- package/dist/src/install/workspace-scope.test.d.ts.map +1 -0
- package/dist/src/install/workspace-scope.test.js +378 -0
- package/dist/src/install/workspace-scope.test.js.map +1 -0
- package/dist/src/security/__tests__/pathValidation.test.js +30 -1
- package/dist/src/security/__tests__/pathValidation.test.js.map +1 -1
- package/dist/src/services/SubagentGenerator.client-profiles.d.ts +258 -0
- package/dist/src/services/SubagentGenerator.client-profiles.d.ts.map +1 -0
- package/dist/src/services/SubagentGenerator.client-profiles.js +260 -0
- package/dist/src/services/SubagentGenerator.client-profiles.js.map +1 -0
- package/dist/src/services/SubagentGenerator.d.ts +19 -41
- package/dist/src/services/SubagentGenerator.d.ts.map +1 -1
- package/dist/src/services/SubagentGenerator.helpers.d.ts +64 -0
- package/dist/src/services/SubagentGenerator.helpers.d.ts.map +1 -0
- package/dist/src/services/SubagentGenerator.helpers.js +228 -0
- package/dist/src/services/SubagentGenerator.helpers.js.map +1 -0
- package/dist/src/services/SubagentGenerator.js +66 -208
- package/dist/src/services/SubagentGenerator.js.map +1 -1
- package/dist/src/services/SubagentGenerator.types.d.ts +45 -0
- package/dist/src/services/SubagentGenerator.types.d.ts.map +1 -0
- package/dist/src/services/SubagentGenerator.types.js +14 -0
- package/dist/src/services/SubagentGenerator.types.js.map +1 -0
- package/dist/src/services/TransformationService.d.ts +18 -61
- package/dist/src/services/TransformationService.d.ts.map +1 -1
- package/dist/src/services/TransformationService.js +29 -17
- package/dist/src/services/TransformationService.js.map +1 -1
- package/dist/src/services/TransformationService.types.d.ts +74 -0
- package/dist/src/services/TransformationService.types.d.ts.map +1 -0
- package/dist/src/services/TransformationService.types.js +8 -0
- package/dist/src/services/TransformationService.types.js.map +1 -0
- package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.d.ts +5 -0
- package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.d.ts.map +1 -0
- package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.js +72 -0
- package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.js.map +1 -0
- package/dist/src/services/__tests__/SubagentGenerator.helpers.test.d.ts +2 -0
- package/dist/src/services/__tests__/SubagentGenerator.helpers.test.d.ts.map +1 -0
- package/dist/src/services/__tests__/SubagentGenerator.helpers.test.js +61 -0
- package/dist/src/services/__tests__/SubagentGenerator.helpers.test.js.map +1 -0
- package/dist/src/services/__tests__/SubagentGenerator.test.js +155 -0
- package/dist/src/services/__tests__/SubagentGenerator.test.js.map +1 -1
- package/dist/src/services/__tests__/TransformationService.test.js +48 -0
- package/dist/src/services/__tests__/TransformationService.test.js.map +1 -1
- package/dist/src/services/agent-pack/agent-pack.test.js +1 -0
- package/dist/src/services/agent-pack/agent-pack.test.js.map +1 -1
- package/dist/src/services/agent-pack/prompt-source.d.ts +1 -1
- package/dist/src/services/agent-pack/prompt-source.d.ts.map +1 -1
- package/dist/src/services/agent-pack/prompt-source.js +10 -3
- package/dist/src/services/agent-pack/prompt-source.js.map +1 -1
- package/dist/src/services/agent-pack/types.d.ts +26 -2
- package/dist/src/services/agent-pack/types.d.ts.map +1 -1
- package/dist/src/services/agent-pack/types.js +13 -0
- package/dist/src/services/agent-pack/types.js.map +1 -1
- package/dist/src/services/agent-tool-profile.d.ts.map +1 -1
- package/dist/src/services/agent-tool-profile.js +7 -0
- package/dist/src/services/agent-tool-profile.js.map +1 -1
- package/dist/src/services/agent-tool-profile.test.js +2 -2
- package/dist/src/services/skill-content-comparison.d.ts +71 -0
- package/dist/src/services/skill-content-comparison.d.ts.map +1 -0
- package/dist/src/services/skill-content-comparison.js +90 -0
- package/dist/src/services/skill-content-comparison.js.map +1 -0
- package/dist/src/services/skill-content-comparison.test.d.ts +6 -0
- package/dist/src/services/skill-content-comparison.test.d.ts.map +1 -0
- package/dist/src/services/skill-content-comparison.test.js +83 -0
- package/dist/src/services/skill-content-comparison.test.js.map +1 -0
- package/dist/src/services/skill-identity-classification.d.ts +202 -0
- package/dist/src/services/skill-identity-classification.d.ts.map +1 -0
- package/dist/src/services/skill-identity-classification.js +295 -0
- package/dist/src/services/skill-identity-classification.js.map +1 -0
- package/dist/src/services/skill-identity-classification.test.d.ts +6 -0
- package/dist/src/services/skill-identity-classification.test.d.ts.map +1 -0
- package/dist/src/services/skill-identity-classification.test.js +314 -0
- package/dist/src/services/skill-identity-classification.test.js.map +1 -0
- package/dist/src/services/skill-installation.content.d.ts.map +1 -1
- package/dist/src/services/skill-installation.content.js +6 -1
- package/dist/src/services/skill-installation.content.js.map +1 -1
- package/dist/src/services/skill-installation.helpers.d.ts +8 -15
- package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.js +11 -105
- package/dist/src/services/skill-installation.helpers.js.map +1 -1
- package/dist/src/services/skill-installation.helpers.test.js +106 -1
- package/dist/src/services/skill-installation.helpers.test.js.map +1 -1
- package/dist/src/services/skill-installation.service.js +1 -1
- package/dist/src/services/skill-installation.service.js.map +1 -1
- package/dist/src/services/skill-installation.types.d.ts +33 -0
- package/dist/src/services/skill-installation.types.d.ts.map +1 -1
- package/dist/src/services/skill-installation.types.js.map +1 -1
- package/dist/src/services/skill-installation.uninstall.d.ts +84 -0
- package/dist/src/services/skill-installation.uninstall.d.ts.map +1 -0
- package/dist/src/services/skill-installation.uninstall.js +252 -0
- package/dist/src/services/skill-installation.uninstall.js.map +1 -0
- package/dist/src/services/skill-manifest.d.ts +41 -0
- package/dist/src/services/skill-manifest.d.ts.map +1 -1
- package/dist/src/services/skill-manifest.js +93 -0
- package/dist/src/services/skill-manifest.js.map +1 -1
- package/dist/src/services/skill-manifest.test.js +27 -0
- package/dist/src/services/skill-manifest.test.js.map +1 -1
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +69 -77
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/telemetry/agent-marker.d.ts +8 -0
- package/dist/src/telemetry/agent-marker.d.ts.map +1 -1
- package/dist/src/telemetry/agent-marker.js +7 -0
- package/dist/src/telemetry/agent-marker.js.map +1 -1
- package/dist/src/telemetry/agent-marker.test.js +7 -1
- package/dist/src/telemetry/agent-marker.test.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +1 -1
- package/dist/src/telemetry/index.d.ts.map +1 -1
- package/dist/src/telemetry/index.js +1 -1
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/wrap.d.ts +6 -0
- package/dist/src/telemetry/wrap.d.ts.map +1 -1
- package/dist/src/telemetry/wrap.js +65 -10
- package/dist/src/telemetry/wrap.js.map +1 -1
- package/dist/src/telemetry/wrap.test.js +81 -1
- package/dist/src/telemetry/wrap.test.js.map +1 -1
- package/dist/src/telemetry/wrap.tool-call-sink.test.d.ts +26 -0
- package/dist/src/telemetry/wrap.tool-call-sink.test.d.ts.map +1 -0
- package/dist/src/telemetry/wrap.tool-call-sink.test.js +162 -0
- package/dist/src/telemetry/wrap.tool-call-sink.test.js.map +1 -0
- package/dist/src/types/skill.d.ts +9 -1
- package/dist/src/types/skill.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +1 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +1 -1
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/version-check.d.ts +61 -0
- package/dist/src/utils/version-check.d.ts.map +1 -1
- package/dist/src/utils/version-check.js +78 -1
- package/dist/src/utils/version-check.js.map +1 -1
- package/dist/src/utils/version-check.test.js +130 -1
- package/dist/src/utils/version-check.test.js.map +1 -1
- package/dist/tests/db/migration-runner-race.test.js +19 -9
- package/dist/tests/db/migration-runner-race.test.js.map +1 -1
- package/dist/tests/install/fan-out.test.js +27 -0
- package/dist/tests/install/fan-out.test.js.map +1 -1
- package/dist/tests/sync/SyncEngine.abort.test.js +15 -11
- package/dist/tests/sync/SyncEngine.abort.test.js.map +1 -1
- package/dist/tests/sync/SyncEngine.status.test.js +1 -1
- package/dist/tests/sync/SyncEngine.status.test.js.map +1 -1
- package/dist/tests/sync/SyncEngine.test.d.ts +11 -2
- package/dist/tests/sync/SyncEngine.test.d.ts.map +1 -1
- package/dist/tests/sync/SyncEngine.test.js +164 -13
- package/dist/tests/sync/SyncEngine.test.js.map +1 -1
- package/dist/tests/unit/migrations/migration-v18.test.d.ts +16 -0
- package/dist/tests/unit/migrations/migration-v18.test.d.ts.map +1 -0
- package/dist/tests/unit/migrations/migration-v18.test.js +192 -0
- package/dist/tests/unit/migrations/migration-v18.test.js.map +1 -0
- package/dist/tests/unit/services/skill-installation.content.test.js +65 -1
- package/dist/tests/unit/services/skill-installation.content.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.service.test.js +9 -8
- package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
- package/dist/tests/unit/sync-engine.source-aware.test.js +5 -5
- package/dist/tests/unit/sync-engine.source-aware.test.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared skill-identity contradiction-signal classification
|
|
3
|
+
* @module @skillsmith/core/services/skill-identity-classification
|
|
4
|
+
* @see SMI-6343 Wave 3 — tamper-check classification (AC#3)
|
|
5
|
+
*
|
|
6
|
+
* Three deterministic-or-best-effort "does this manifest entry's recorded
|
|
7
|
+
* identity contradict what's actually installed" signals, plus a
|
|
8
|
+
* "has this on-disk content been locally edited since install" check —
|
|
9
|
+
* consumed by BOTH `packages/mcp-server/src/tools/outdated.ts` (via
|
|
10
|
+
* `outdated.identity.ts`) and `packages/cli/src/commands/manage.update.ts`,
|
|
11
|
+
* so the two packages cannot drift into two independently-maintained
|
|
12
|
+
* implementations of the same contradiction logic (exactly the "sibling
|
|
13
|
+
* implementation" bug class both Wave 1 and Wave 2 of SMI-6343 already hit).
|
|
14
|
+
*
|
|
15
|
+
* Deliberately decoupled from either caller's own `RegistrySkillInfo` shape
|
|
16
|
+
* (mcp-server's `install.types.ts` and core's own `skill-installation.types.ts`
|
|
17
|
+
* both have independently-evolved versions) — callers adapt their own
|
|
18
|
+
* registry-lookup result into the minimal {@link RegistryLookupOutcome} shape
|
|
19
|
+
* this module needs.
|
|
20
|
+
*
|
|
21
|
+
* Every function here is synchronous and filesystem-free by design: signal 3
|
|
22
|
+
* (path containment) is a pure string check against an already-resolved
|
|
23
|
+
* expected root directory, not a `fs.realpath` call — the callers of this
|
|
24
|
+
* module (`outdated.ts`, `manage.update.ts`) only ever reach the "outdated"
|
|
25
|
+
* classification branch after already having successfully read the
|
|
26
|
+
* installed skill's SKILL.md this same iteration, so "does installPath
|
|
27
|
+
* exist" is already established by the time these signals run.
|
|
28
|
+
*/
|
|
29
|
+
import { relative, isAbsolute, sep } from 'node:path';
|
|
30
|
+
import { SkillParser } from '../indexer/SkillParser.js';
|
|
31
|
+
import { firstNonBlankHash } from './skill-content-comparison.js';
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Signal 1 — owner mismatch
|
|
34
|
+
// ============================================================================
|
|
35
|
+
/**
|
|
36
|
+
* Parse a clean `owner/name` pair (exactly one slash, no URL scheme) into
|
|
37
|
+
* its owner segment. Returns `null` for anything that isn't that exact
|
|
38
|
+
* shape — critically, a raw URL (`https://github.com/owner/repo`, a
|
|
39
|
+
* direct-URL install's `id`/`source`) is NOT an `owner/name` pair and must
|
|
40
|
+
* return `null` here rather than a garbage "owner" parsed from before the
|
|
41
|
+
* URL's first slash (e.g. `'https:'`). Mirrors the same 2-segment
|
|
42
|
+
* distinction `install.helpers.ts`'s `parseSkillId()` already draws between
|
|
43
|
+
* a registry id and a direct GitHub reference.
|
|
44
|
+
*/
|
|
45
|
+
function parseOwnerFromOwnerNamePair(value) {
|
|
46
|
+
if (value.includes('://'))
|
|
47
|
+
return null;
|
|
48
|
+
const parts = value.split('/');
|
|
49
|
+
if (parts.length !== 2)
|
|
50
|
+
return null;
|
|
51
|
+
const [owner, name] = parts;
|
|
52
|
+
return owner.length > 0 && name.length > 0 ? owner : null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Parse the owner segment out of a manifest `source` string. Real installs
|
|
56
|
+
* write `source` as `'github:' + owner + '/' + repo` (`skill-installation.
|
|
57
|
+
* service.ts`) — the `'github:'` prefix is optional here so a bare
|
|
58
|
+
* `owner/repo` (or a future non-GitHub source shape) still parses. Returns
|
|
59
|
+
* `null` for the `'unknown'` distrust sentinel, a raw URL, or anything else
|
|
60
|
+
* with no parseable `owner/name` shape.
|
|
61
|
+
*/
|
|
62
|
+
export function parseOwnerFromSource(source) {
|
|
63
|
+
if (!source || source === 'unknown')
|
|
64
|
+
return null;
|
|
65
|
+
const withoutPrefix = source.startsWith('github:') ? source.slice('github:'.length) : source;
|
|
66
|
+
return parseOwnerFromOwnerNamePair(withoutPrefix);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse the owner segment out of a manifest entry's `id`. Returns `null`
|
|
70
|
+
* for anything that isn't a clean `owner/name` pair — most importantly a
|
|
71
|
+
* raw GitHub URL, which a direct-URL install records as `id` verbatim (see
|
|
72
|
+
* {@link parseOwnerFromOwnerNamePair}).
|
|
73
|
+
*/
|
|
74
|
+
export function parseOwnerFromId(id) {
|
|
75
|
+
if (!id)
|
|
76
|
+
return null;
|
|
77
|
+
return parseOwnerFromOwnerNamePair(id);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Signal 1: `id` parses as `owner/name` and `source` names a DIFFERENT
|
|
81
|
+
* owner. Deterministic, offline, zero network dependency. Catches the real
|
|
82
|
+
* `linear` skill corruption (`source: "github:lobehub/lobehub"` vs
|
|
83
|
+
* `id: "wrsmith108/linear"`).
|
|
84
|
+
*/
|
|
85
|
+
export function detectOwnerMismatch(entry) {
|
|
86
|
+
const idOwner = parseOwnerFromId(entry.id);
|
|
87
|
+
const sourceOwner = parseOwnerFromSource(entry.source);
|
|
88
|
+
if (!idOwner || !sourceOwner)
|
|
89
|
+
return false;
|
|
90
|
+
return idOwner.toLowerCase() !== sourceOwner.toLowerCase();
|
|
91
|
+
}
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Signal 3 — path unresolved
|
|
94
|
+
// ============================================================================
|
|
95
|
+
/**
|
|
96
|
+
* Signal 3: `installPath` is absent, or resolves OUTSIDE `expectedRootDir`
|
|
97
|
+
* (the claimed client's native install root, or a workspace-scoped
|
|
98
|
+
* equivalent the caller resolves — see each caller's own scope handling).
|
|
99
|
+
*
|
|
100
|
+
* Deliberately a pure string containment check (`path.relative`), not a
|
|
101
|
+
* filesystem call: both callers of this module only reach this signal after
|
|
102
|
+
* already having successfully read the entry's SKILL.md this same
|
|
103
|
+
* iteration, so filesystem existence is already established. This also
|
|
104
|
+
* catches the exact shape of the SMI-6343 Wave 1 test-fixture leak (an
|
|
105
|
+
* OS-temp-dir `installPath` written into the real manifest) via simple
|
|
106
|
+
* string comparison, with no need to resolve symlinks.
|
|
107
|
+
*
|
|
108
|
+
* NOT a symlink-escape check (Wave 3 adversarial review, considered and
|
|
109
|
+
* scoped out): a real `installPath` inside `expectedRootDir` that is
|
|
110
|
+
* ITSELF a symlink pointing elsewhere is a local-filesystem-tampering
|
|
111
|
+
* threat distinct from this signal's actual purpose (catching a manifest
|
|
112
|
+
* entry whose *recorded* path doesn't match a legitimate install
|
|
113
|
+
* location — a software-bug/registry-tamper shape, not local attacker
|
|
114
|
+
* capability); an attacker with write access to plant such a symlink
|
|
115
|
+
* inside the client root already has equivalent-or-greater capability to
|
|
116
|
+
* edit SKILL.md content directly, making a `fs.realpath` upgrade here
|
|
117
|
+
* (which would also force this whole synchronous, filesystem-free module
|
|
118
|
+
* to become async) address a threat this signal was never meant to cover.
|
|
119
|
+
*
|
|
120
|
+
* The parent-segment check below (`rel === '..' || rel.startsWith('..' +
|
|
121
|
+
* sep)`) is deliberately NOT a bare `rel.startsWith('..')`: a literal
|
|
122
|
+
* directory name that happens to start with two dots (e.g. `..cache`) is a
|
|
123
|
+
* legitimate contained child — `path.relative('/r', '/r/..cache')` returns
|
|
124
|
+
* `'..cache'`, which a bare `startsWith('..')` would misclassify as an
|
|
125
|
+
* escape (Wave 3 adversarial review finding).
|
|
126
|
+
*/
|
|
127
|
+
export function detectPathUnresolved(installPath, expectedRootDir) {
|
|
128
|
+
if (!installPath)
|
|
129
|
+
return true;
|
|
130
|
+
const rel = relative(expectedRootDir, installPath);
|
|
131
|
+
if (rel === '')
|
|
132
|
+
return false;
|
|
133
|
+
return rel === '..' || rel.startsWith('..' + sep) || isAbsolute(rel);
|
|
134
|
+
}
|
|
135
|
+
function normalize(value) {
|
|
136
|
+
if (typeof value !== 'string')
|
|
137
|
+
return null;
|
|
138
|
+
const trimmed = value.trim();
|
|
139
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Signal 2: on-disk front-matter `author`/`name` contradicts the registry's
|
|
143
|
+
* record for the claimed `id`. Catches the real `commit` skill corruption
|
|
144
|
+
* (internally-consistent id/source, but content that belongs to an
|
|
145
|
+
* unrelated skill) — signal 1 alone does not catch this shape.
|
|
146
|
+
*
|
|
147
|
+
* H2 (fail-closed): when the registry lookup could not complete (offline,
|
|
148
|
+
* network error, quota-exhausted) OR completed but found no record for the
|
|
149
|
+
* claimed id, this returns `inconclusive: true` — NEVER "no contradiction."
|
|
150
|
+
* An inconclusive signal 2 still forces the overall classification to
|
|
151
|
+
* `unknown`, not `local-drift`/`current` — see {@link classifyDivergentEntry}.
|
|
152
|
+
*
|
|
153
|
+
* Exception, not a weakening of H2: when `id` itself doesn't parse as a
|
|
154
|
+
* well-formed `owner/name` registry key (a raw GitHub URL from a direct-URL
|
|
155
|
+
* install, an unresolved UUID) there is structurally no registry record to
|
|
156
|
+
* look up in the first place — a caller cannot "fail to check" something
|
|
157
|
+
* that was never a registry-backed identity to begin with. This is a clean
|
|
158
|
+
* non-fire, not an inconclusive one, exactly mirroring how an unparseable
|
|
159
|
+
* on-disk front-matter is treated below.
|
|
160
|
+
*/
|
|
161
|
+
function evaluateFrontmatterContradiction(entryId, localContent, registryLookup) {
|
|
162
|
+
if (parseOwnerFromId(entryId) === null) {
|
|
163
|
+
return { fired: false, inconclusive: false, inconclusiveReason: null };
|
|
164
|
+
}
|
|
165
|
+
if (!registryLookup.attempted) {
|
|
166
|
+
return {
|
|
167
|
+
fired: false,
|
|
168
|
+
inconclusive: true,
|
|
169
|
+
inconclusiveReason: registryLookup.failureReason ?? 'network-error',
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (registryLookup.failureReason) {
|
|
173
|
+
return { fired: false, inconclusive: true, inconclusiveReason: registryLookup.failureReason };
|
|
174
|
+
}
|
|
175
|
+
if (!registryLookup.record) {
|
|
176
|
+
return { fired: false, inconclusive: true, inconclusiveReason: 'no-registry-record' };
|
|
177
|
+
}
|
|
178
|
+
const parsed = localContent !== null ? new SkillParser().parse(localContent) : null;
|
|
179
|
+
if (!parsed) {
|
|
180
|
+
// Nothing to contradict with — a local parse failure is a data-quality
|
|
181
|
+
// fact, not a network-dependent uncertainty, so this is a clean
|
|
182
|
+
// non-fire, not `inconclusive` (see this module's fileoverview).
|
|
183
|
+
return { fired: false, inconclusive: false, inconclusiveReason: null };
|
|
184
|
+
}
|
|
185
|
+
const localAuthor = normalize(parsed.author);
|
|
186
|
+
const localName = normalize(parsed.name);
|
|
187
|
+
const registryAuthor = normalize(registryLookup.record.author);
|
|
188
|
+
const registryName = normalize(registryLookup.record.name);
|
|
189
|
+
const authorContradiction = localAuthor !== null &&
|
|
190
|
+
registryAuthor !== null &&
|
|
191
|
+
localAuthor.toLowerCase() !== registryAuthor.toLowerCase();
|
|
192
|
+
const nameContradiction = localName !== null &&
|
|
193
|
+
registryName !== null &&
|
|
194
|
+
localName.toLowerCase() !== registryName.toLowerCase();
|
|
195
|
+
return {
|
|
196
|
+
fired: authorContradiction || nameContradiction,
|
|
197
|
+
inconclusive: false,
|
|
198
|
+
inconclusiveReason: null,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// ============================================================================
|
|
202
|
+
// Combined signal classification
|
|
203
|
+
// ============================================================================
|
|
204
|
+
/**
|
|
205
|
+
* Run all three contradiction signals against a manifest entry. Any one
|
|
206
|
+
* conclusively firing means `signal` is non-null — order (1, 3, 2) checks
|
|
207
|
+
* the two deterministic/offline signals first, only falling to the
|
|
208
|
+
* network-dependent signal 2 when neither of those already answered the
|
|
209
|
+
* question.
|
|
210
|
+
*/
|
|
211
|
+
export function classifyManifestEntryIdentity(params) {
|
|
212
|
+
const { entry, localContent, expectedRootDir, registryLookup } = params;
|
|
213
|
+
if (detectOwnerMismatch(entry)) {
|
|
214
|
+
return { signal: 'owner-mismatch', inconclusive: false, inconclusiveReason: null };
|
|
215
|
+
}
|
|
216
|
+
if (detectPathUnresolved(entry.installPath, expectedRootDir)) {
|
|
217
|
+
return { signal: 'path-unresolved', inconclusive: false, inconclusiveReason: null };
|
|
218
|
+
}
|
|
219
|
+
const signal2 = evaluateFrontmatterContradiction(entry.id, localContent, registryLookup);
|
|
220
|
+
if (signal2.inconclusive) {
|
|
221
|
+
return { signal: null, inconclusive: true, inconclusiveReason: signal2.inconclusiveReason };
|
|
222
|
+
}
|
|
223
|
+
if (signal2.fired) {
|
|
224
|
+
return { signal: 'frontmatter-contradiction', inconclusive: false, inconclusiveReason: null };
|
|
225
|
+
}
|
|
226
|
+
return { signal: null, inconclusive: false, inconclusiveReason: null };
|
|
227
|
+
}
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// Local-edit detection (local-drift vs outdated)
|
|
230
|
+
// ============================================================================
|
|
231
|
+
/**
|
|
232
|
+
* Has the on-disk content changed since the manifest's own recorded
|
|
233
|
+
* install/update-time hash? This is what distinguishes a benign local edit
|
|
234
|
+
* (`local-drift`) from a genuine registry version bump (`outdated`) once
|
|
235
|
+
* no identity signal has fired.
|
|
236
|
+
*
|
|
237
|
+
* When the manifest never recorded a comparable hash at all (a legacy or
|
|
238
|
+
* adopted entry, `contentHash`/`originalContentHash` both absent), this
|
|
239
|
+
* deliberately defaults to `false` (no edit evidence) rather than `true`
|
|
240
|
+
* (conservative) — absence of a recorded hash is a data-quality gap, not
|
|
241
|
+
* positive evidence of tampering, and defaulting to `true` here would
|
|
242
|
+
* misclassify the large population of legacy/adopted entries that never had
|
|
243
|
+
* a hash recorded as `local-drift` even when they are genuinely just
|
|
244
|
+
* outdated (SMI-6343 Wave 3 review finding).
|
|
245
|
+
*/
|
|
246
|
+
export function hasRecordedLocalEdit(entry, localHash) {
|
|
247
|
+
const recordedHash = firstNonBlankHash(entry.contentHash, entry.originalContentHash);
|
|
248
|
+
if (recordedHash === null || localHash === null)
|
|
249
|
+
return false;
|
|
250
|
+
return recordedHash !== localHash;
|
|
251
|
+
}
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// Full divergent-entry classification
|
|
254
|
+
// ============================================================================
|
|
255
|
+
/**
|
|
256
|
+
* Classify an entry the caller has ALREADY determined is divergent from the
|
|
257
|
+
* registry (mcp-server: `compareSkillContentHashes(...).outcome ===
|
|
258
|
+
* 'outdated'`; CLI: `getSkillDiff()` found a version/content difference).
|
|
259
|
+
* Never returns `'current'` — callers own that trivial case themselves,
|
|
260
|
+
* since it never needs signal evaluation at all.
|
|
261
|
+
*/
|
|
262
|
+
export function classifyDivergentEntry(params) {
|
|
263
|
+
const identity = classifyManifestEntryIdentity(params);
|
|
264
|
+
if (identity.signal) {
|
|
265
|
+
return { state: 'identity-mismatch', signal: identity.signal, inconclusiveReason: null };
|
|
266
|
+
}
|
|
267
|
+
if (identity.inconclusive) {
|
|
268
|
+
return { state: 'unknown', signal: null, inconclusiveReason: identity.inconclusiveReason };
|
|
269
|
+
}
|
|
270
|
+
const hasLocalEdit = hasRecordedLocalEdit(params.entry, params.localHash);
|
|
271
|
+
return hasLocalEdit
|
|
272
|
+
? { state: 'local-drift', signal: null, inconclusiveReason: null }
|
|
273
|
+
: { state: 'outdated', signal: null, inconclusiveReason: null };
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Full 5-state classification given an already-computed content-comparison
|
|
277
|
+
* outcome (from `compareSkillContentHashes`) plus the caller-derived reason
|
|
278
|
+
* a plain `'unknown'` comparison outcome couldn't be resolved (offline,
|
|
279
|
+
* quota-exhausted, network-error, or no-history — the caller already tracks
|
|
280
|
+
* which applies; see `outdated.ts`'s `deriveUnknownReason`).
|
|
281
|
+
*/
|
|
282
|
+
export function classifyOutdatedState(params) {
|
|
283
|
+
if (params.comparisonOutcome === 'current') {
|
|
284
|
+
return { state: 'current', signal: null, inconclusiveReason: null };
|
|
285
|
+
}
|
|
286
|
+
if (params.comparisonOutcome === 'unknown') {
|
|
287
|
+
return {
|
|
288
|
+
state: 'unknown',
|
|
289
|
+
signal: null,
|
|
290
|
+
inconclusiveReason: params.unknownReasonWhenComparisonUnknown,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return classifyDivergentEntry(params);
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=skill-identity-classification.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-identity-classification.js","sourceRoot":"","sources":["../../../src/services/skill-identity-classification.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-identity-classification.test.d.ts","sourceRoot":"","sources":["../../../src/services/skill-identity-classification.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Unit tests for the shared skill-identity classification module
|
|
3
|
+
* @see SMI-6343 Wave 3 — tamper-check classification (AC#3)
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { parseOwnerFromSource, parseOwnerFromId, detectOwnerMismatch, detectPathUnresolved, hasRecordedLocalEdit, classifyManifestEntryIdentity, classifyDivergentEntry, classifyOutdatedState, } from './skill-identity-classification.js';
|
|
7
|
+
function entry(overrides = {}) {
|
|
8
|
+
return {
|
|
9
|
+
id: 'wrsmith108/linear',
|
|
10
|
+
source: 'github:wrsmith108/linear',
|
|
11
|
+
installPath: '/home/user/.claude/skills/linear',
|
|
12
|
+
...overrides,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
const notAttempted = { attempted: false, record: null, failureReason: null };
|
|
16
|
+
describe('parseOwnerFromSource', () => {
|
|
17
|
+
it('parses the owner out of a github: prefixed source', () => {
|
|
18
|
+
expect(parseOwnerFromSource('github:lobehub/lobehub')).toBe('lobehub');
|
|
19
|
+
});
|
|
20
|
+
it('parses a bare owner/repo without the prefix', () => {
|
|
21
|
+
expect(parseOwnerFromSource('acme/widget')).toBe('acme');
|
|
22
|
+
});
|
|
23
|
+
it('returns null for the "unknown" distrust sentinel', () => {
|
|
24
|
+
expect(parseOwnerFromSource('unknown')).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
it('returns null for a source with no parseable owner segment', () => {
|
|
27
|
+
expect(parseOwnerFromSource('registry')).toBeNull();
|
|
28
|
+
expect(parseOwnerFromSource('')).toBeNull();
|
|
29
|
+
expect(parseOwnerFromSource(null)).toBeNull();
|
|
30
|
+
expect(parseOwnerFromSource(undefined)).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
it('returns null for a raw URL source with no github: prefix', () => {
|
|
33
|
+
expect(parseOwnerFromSource('https://github.com/owner/repo')).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('parseOwnerFromId', () => {
|
|
37
|
+
it('parses the owner out of an owner/name id', () => {
|
|
38
|
+
expect(parseOwnerFromId('wrsmith108/linear')).toBe('wrsmith108');
|
|
39
|
+
});
|
|
40
|
+
it('returns null for a bare name with no slash', () => {
|
|
41
|
+
expect(parseOwnerFromId('linear')).toBeNull();
|
|
42
|
+
});
|
|
43
|
+
it('returns null for null/undefined', () => {
|
|
44
|
+
expect(parseOwnerFromId(null)).toBeNull();
|
|
45
|
+
expect(parseOwnerFromId(undefined)).toBeNull();
|
|
46
|
+
});
|
|
47
|
+
it('returns null for a raw GitHub URL id (direct-URL install) — regression: must not parse "https:" as the owner', () => {
|
|
48
|
+
expect(parseOwnerFromId('https://github.com/owner-b/test-repo')).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
it('returns null for a 3+-segment owner/repo/path shape', () => {
|
|
51
|
+
expect(parseOwnerFromId('owner/repo/path')).toBeNull();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('detectOwnerMismatch (signal 1)', () => {
|
|
55
|
+
it('fires when id and source name different owners — the real `linear` incident shape', () => {
|
|
56
|
+
expect(detectOwnerMismatch({ id: 'wrsmith108/linear', source: 'github:lobehub/lobehub' })).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
it('does not fire when owners agree', () => {
|
|
59
|
+
expect(detectOwnerMismatch({ id: 'wrsmith108/linear', source: 'github:wrsmith108/linear' })).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
it('does not fire (case-insensitive) when owners agree only up to case', () => {
|
|
62
|
+
expect(detectOwnerMismatch({ id: 'WrSmith108/linear', source: 'github:wrsmith108/linear' })).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
it('does not fire when source is the "unknown" sentinel', () => {
|
|
65
|
+
expect(detectOwnerMismatch({ id: 'wrsmith108/linear', source: 'unknown' })).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
it('does not fire when id has no owner segment', () => {
|
|
68
|
+
expect(detectOwnerMismatch({ id: 'linear', source: 'github:lobehub/lobehub' })).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
it('does not fire for a direct-URL install (id is the raw URL itself) — regression found against a real CLI multi-client fixture', () => {
|
|
71
|
+
expect(detectOwnerMismatch({
|
|
72
|
+
id: 'https://github.com/owner-b/test-repo',
|
|
73
|
+
source: 'github:owner-b/test-repo',
|
|
74
|
+
})).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe('detectPathUnresolved (signal 3)', () => {
|
|
78
|
+
const root = '/home/user/.claude/skills';
|
|
79
|
+
it('does not fire for a path inside the expected root', () => {
|
|
80
|
+
expect(detectPathUnresolved(`${root}/astro`, root)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
it('fires for a path outside the expected root — the fixture-leak shape', () => {
|
|
83
|
+
expect(detectPathUnresolved('/tmp/skillsmith-test-abc123/fixture', root)).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
it('fires for a missing installPath', () => {
|
|
86
|
+
expect(detectPathUnresolved(undefined, root)).toBe(true);
|
|
87
|
+
expect(detectPathUnresolved('', root)).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
it('does not fire when installPath equals the root itself', () => {
|
|
90
|
+
expect(detectPathUnresolved(root, root)).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
it('does NOT fire for a contained child whose name merely starts with two dots (adversarial-review regression)', () => {
|
|
93
|
+
// path.relative('/root', '/root/..cache') === '..cache' — a literal,
|
|
94
|
+
// legitimate directory name, not a parent-directory escape. A bare
|
|
95
|
+
// `rel.startsWith('..')` check would misclassify this as outside.
|
|
96
|
+
expect(detectPathUnresolved(`${root}/..cache`, root)).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
it('fires for an actual parent-directory escape one level up', () => {
|
|
99
|
+
expect(detectPathUnresolved('/home/user/.claude/other', root)).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('hasRecordedLocalEdit', () => {
|
|
103
|
+
it('returns true when the on-disk hash differs from the recorded hash', () => {
|
|
104
|
+
expect(hasRecordedLocalEdit({ contentHash: 'aaa' }, 'bbb')).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
it('returns false when the on-disk hash matches the recorded hash', () => {
|
|
107
|
+
expect(hasRecordedLocalEdit({ contentHash: 'aaa' }, 'aaa')).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
it('falls back to originalContentHash when contentHash is absent', () => {
|
|
110
|
+
expect(hasRecordedLocalEdit({ originalContentHash: 'aaa' }, 'bbb')).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it('defaults to false (no evidence of edit) when neither hash was ever recorded', () => {
|
|
113
|
+
expect(hasRecordedLocalEdit({}, 'bbb')).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
it('defaults to false when the local hash itself is unavailable', () => {
|
|
116
|
+
expect(hasRecordedLocalEdit({ contentHash: 'aaa' }, null)).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('classifyManifestEntryIdentity — signal 2 (frontmatter contradiction)', () => {
|
|
120
|
+
const frontmatterContent = [
|
|
121
|
+
'---',
|
|
122
|
+
'name: commit',
|
|
123
|
+
'author: some-unrelated-author',
|
|
124
|
+
'---',
|
|
125
|
+
'# Commit skill',
|
|
126
|
+
'Sentry commit-message conventions.',
|
|
127
|
+
].join('\n');
|
|
128
|
+
it('fires when on-disk author contradicts the registry record for the claimed id', () => {
|
|
129
|
+
const result = classifyManifestEntryIdentity({
|
|
130
|
+
entry: entry({ id: 'acme/commit', source: 'github:acme/commit' }),
|
|
131
|
+
localContent: frontmatterContent,
|
|
132
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
133
|
+
registryLookup: { attempted: true, record: { author: 'acme', name: 'commit' } },
|
|
134
|
+
});
|
|
135
|
+
expect(result.signal).toBe('frontmatter-contradiction');
|
|
136
|
+
expect(result.inconclusive).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
it('does not fire when on-disk author agrees with the registry record', () => {
|
|
139
|
+
const result = classifyManifestEntryIdentity({
|
|
140
|
+
entry: entry({
|
|
141
|
+
id: 'some-unrelated-author/commit',
|
|
142
|
+
source: 'github:some-unrelated-author/commit',
|
|
143
|
+
}),
|
|
144
|
+
localContent: frontmatterContent,
|
|
145
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
146
|
+
registryLookup: {
|
|
147
|
+
attempted: true,
|
|
148
|
+
record: { author: 'some-unrelated-author', name: 'commit' },
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
expect(result.signal).toBeNull();
|
|
152
|
+
expect(result.inconclusive).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
it('is inconclusive (never "no contradiction") when the lookup was not attempted — offline', () => {
|
|
155
|
+
const result = classifyManifestEntryIdentity({
|
|
156
|
+
entry: entry(),
|
|
157
|
+
localContent: frontmatterContent,
|
|
158
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
159
|
+
registryLookup: { attempted: false, record: null, failureReason: 'offline' },
|
|
160
|
+
});
|
|
161
|
+
expect(result.signal).toBeNull();
|
|
162
|
+
expect(result.inconclusive).toBe(true);
|
|
163
|
+
expect(result.inconclusiveReason).toBe('offline');
|
|
164
|
+
});
|
|
165
|
+
it('is inconclusive when quota-exhausted', () => {
|
|
166
|
+
const result = classifyManifestEntryIdentity({
|
|
167
|
+
entry: entry(),
|
|
168
|
+
localContent: frontmatterContent,
|
|
169
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
170
|
+
registryLookup: { attempted: false, record: null, failureReason: 'quota-exhausted' },
|
|
171
|
+
});
|
|
172
|
+
expect(result.inconclusive).toBe(true);
|
|
173
|
+
expect(result.inconclusiveReason).toBe('quota-exhausted');
|
|
174
|
+
});
|
|
175
|
+
it('is inconclusive when the lookup was attempted but failed (network error)', () => {
|
|
176
|
+
const result = classifyManifestEntryIdentity({
|
|
177
|
+
entry: entry(),
|
|
178
|
+
localContent: frontmatterContent,
|
|
179
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
180
|
+
registryLookup: { attempted: true, record: null, failureReason: 'network-error' },
|
|
181
|
+
});
|
|
182
|
+
expect(result.inconclusive).toBe(true);
|
|
183
|
+
expect(result.inconclusiveReason).toBe('network-error');
|
|
184
|
+
});
|
|
185
|
+
it('is inconclusive when the lookup completed but found no registry record', () => {
|
|
186
|
+
const result = classifyManifestEntryIdentity({
|
|
187
|
+
entry: entry(),
|
|
188
|
+
localContent: frontmatterContent,
|
|
189
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
190
|
+
registryLookup: { attempted: true, record: null },
|
|
191
|
+
});
|
|
192
|
+
expect(result.inconclusive).toBe(true);
|
|
193
|
+
expect(result.inconclusiveReason).toBe('no-registry-record');
|
|
194
|
+
});
|
|
195
|
+
it('does not fire (and is not inconclusive) when local content has no parseable frontmatter', () => {
|
|
196
|
+
const result = classifyManifestEntryIdentity({
|
|
197
|
+
entry: entry(),
|
|
198
|
+
localContent: 'plain content with no frontmatter',
|
|
199
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
200
|
+
registryLookup: { attempted: true, record: { author: 'wrsmith108', name: 'linear' } },
|
|
201
|
+
});
|
|
202
|
+
expect(result.signal).toBeNull();
|
|
203
|
+
expect(result.inconclusive).toBe(false);
|
|
204
|
+
});
|
|
205
|
+
it('is a clean non-fire (not inconclusive) when id does not parse as owner/name — a raw-URL direct install has no registry key to check', () => {
|
|
206
|
+
const result = classifyManifestEntryIdentity({
|
|
207
|
+
entry: entry({
|
|
208
|
+
id: 'https://github.com/owner-b/test-repo',
|
|
209
|
+
source: 'github:owner-b/test-repo',
|
|
210
|
+
}),
|
|
211
|
+
localContent: frontmatterContent,
|
|
212
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
213
|
+
registryLookup: notAttempted,
|
|
214
|
+
});
|
|
215
|
+
expect(result.signal).toBeNull();
|
|
216
|
+
expect(result.inconclusive).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
it('signal 1 takes precedence over an inconclusive signal 2', () => {
|
|
219
|
+
const result = classifyManifestEntryIdentity({
|
|
220
|
+
entry: entry({ id: 'wrsmith108/linear', source: 'github:lobehub/lobehub' }),
|
|
221
|
+
localContent: frontmatterContent,
|
|
222
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
223
|
+
registryLookup: notAttempted,
|
|
224
|
+
});
|
|
225
|
+
expect(result.signal).toBe('owner-mismatch');
|
|
226
|
+
expect(result.inconclusive).toBe(false);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
describe('classifyDivergentEntry', () => {
|
|
230
|
+
it('classifies as identity-mismatch when a signal fires', () => {
|
|
231
|
+
const result = classifyDivergentEntry({
|
|
232
|
+
entry: entry({ id: 'wrsmith108/linear', source: 'github:lobehub/lobehub' }),
|
|
233
|
+
localHash: 'abc',
|
|
234
|
+
localContent: 'content',
|
|
235
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
236
|
+
registryLookup: notAttempted,
|
|
237
|
+
});
|
|
238
|
+
expect(result.state).toBe('identity-mismatch');
|
|
239
|
+
expect(result.signal).toBe('owner-mismatch');
|
|
240
|
+
});
|
|
241
|
+
it('classifies as unknown when signal 2 is inconclusive', () => {
|
|
242
|
+
const result = classifyDivergentEntry({
|
|
243
|
+
entry: entry(),
|
|
244
|
+
localHash: 'abc',
|
|
245
|
+
localContent: 'content',
|
|
246
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
247
|
+
registryLookup: { attempted: false, record: null, failureReason: 'offline' },
|
|
248
|
+
});
|
|
249
|
+
expect(result.state).toBe('unknown');
|
|
250
|
+
expect(result.inconclusiveReason).toBe('offline');
|
|
251
|
+
});
|
|
252
|
+
it('classifies as local-drift when no signal fires but on-disk content diverges from the recorded hash', () => {
|
|
253
|
+
const result = classifyDivergentEntry({
|
|
254
|
+
entry: entry({ contentHash: 'recorded-hash' }),
|
|
255
|
+
localHash: 'edited-hash',
|
|
256
|
+
localContent: 'content',
|
|
257
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
258
|
+
registryLookup: { attempted: true, record: { author: 'wrsmith108', name: 'linear' } },
|
|
259
|
+
});
|
|
260
|
+
expect(result.state).toBe('local-drift');
|
|
261
|
+
expect(result.signal).toBeNull();
|
|
262
|
+
});
|
|
263
|
+
it('classifies as outdated when no signal fires and no local edit is evidenced', () => {
|
|
264
|
+
const result = classifyDivergentEntry({
|
|
265
|
+
entry: entry(),
|
|
266
|
+
localHash: 'installed-hash',
|
|
267
|
+
localContent: 'content',
|
|
268
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
269
|
+
registryLookup: { attempted: true, record: { author: 'wrsmith108', name: 'linear' } },
|
|
270
|
+
});
|
|
271
|
+
expect(result.state).toBe('outdated');
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
describe('classifyOutdatedState', () => {
|
|
275
|
+
it('returns current without evaluating signals', () => {
|
|
276
|
+
const result = classifyOutdatedState({
|
|
277
|
+
comparisonOutcome: 'current',
|
|
278
|
+
unknownReasonWhenComparisonUnknown: 'no-history',
|
|
279
|
+
entry: entry({ id: 'wrsmith108/linear', source: 'github:lobehub/lobehub' }), // would fire if evaluated
|
|
280
|
+
localHash: 'x',
|
|
281
|
+
localContent: 'content',
|
|
282
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
283
|
+
registryLookup: notAttempted,
|
|
284
|
+
});
|
|
285
|
+
expect(result.state).toBe('current');
|
|
286
|
+
expect(result.signal).toBeNull();
|
|
287
|
+
});
|
|
288
|
+
it('returns unknown with the caller-supplied reason when the comparison itself is unknown', () => {
|
|
289
|
+
const result = classifyOutdatedState({
|
|
290
|
+
comparisonOutcome: 'unknown',
|
|
291
|
+
unknownReasonWhenComparisonUnknown: 'no-history',
|
|
292
|
+
entry: entry(),
|
|
293
|
+
localHash: null,
|
|
294
|
+
localContent: null,
|
|
295
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
296
|
+
registryLookup: notAttempted,
|
|
297
|
+
});
|
|
298
|
+
expect(result.state).toBe('unknown');
|
|
299
|
+
expect(result.inconclusiveReason).toBe('no-history');
|
|
300
|
+
});
|
|
301
|
+
it('delegates to classifyDivergentEntry for an outdated comparison outcome', () => {
|
|
302
|
+
const result = classifyOutdatedState({
|
|
303
|
+
comparisonOutcome: 'outdated',
|
|
304
|
+
unknownReasonWhenComparisonUnknown: 'no-history',
|
|
305
|
+
entry: entry(),
|
|
306
|
+
localHash: 'installed-hash',
|
|
307
|
+
localContent: 'content',
|
|
308
|
+
expectedRootDir: '/home/user/.claude/skills',
|
|
309
|
+
registryLookup: { attempted: true, record: { author: 'wrsmith108', name: 'linear' } },
|
|
310
|
+
});
|
|
311
|
+
expect(result.state).toBe('outdated');
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
//# sourceMappingURL=skill-identity-classification.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-identity-classification.test.js","sourceRoot":"","sources":["../../../src/services/skill-identity-classification.test.ts"],"names":[],"mappings":"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"}
|