@skill-map/cli 0.28.0 → 0.29.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/dist/cli/tutorial/sm-tutorial.md +94 -20
- package/dist/cli.js +1408 -1193
- package/dist/cli.js.map +1 -1
- package/dist/index.js +116 -99
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.d.ts +903 -1004
- package/dist/kernel/index.js +116 -99
- package/dist/kernel/index.js.map +1 -1
- package/dist/ui/chunk-3SI3TVER.js +7 -0
- package/dist/ui/{chunk-4GTCV7V4.js → chunk-47OZB7LR.js} +1 -1
- package/dist/ui/{chunk-JMP2LDMI.js → chunk-5JBW2LUN.js} +1 -1
- package/dist/ui/chunk-BL7KARTN.js +317 -0
- package/dist/ui/chunk-DMSZOXER.js +1 -0
- package/dist/ui/{chunk-Y7MXGXU3.js → chunk-DZBSELHN.js} +1 -1
- package/dist/ui/chunk-EFKSD7PT.js +123 -0
- package/dist/ui/{chunk-Z2667C3S.js → chunk-FEPH4VNB.js} +1 -1
- package/dist/ui/{chunk-PY2R7LHN.js → chunk-FQOZBFJ5.js} +1 -1
- package/dist/ui/{chunk-WOLLYGGL.js → chunk-KJQEO6P3.js} +1 -1
- package/dist/ui/{chunk-VO6NF24F.js → chunk-LS2NXZQZ.js} +1 -1
- package/dist/ui/{chunk-J3YWUNFO.js → chunk-LTQTJU54.js} +1 -1
- package/dist/ui/{chunk-6BG7PBUN.js → chunk-NGIFGXW7.js} +1 -1
- package/dist/ui/{chunk-5W6J6H76.js → chunk-SBCO7ZSP.js} +1 -1
- package/dist/ui/chunk-VB56BUGO.js +1 -0
- package/dist/ui/{chunk-UXCAEDR6.js → chunk-VDQLDTTR.js} +1 -1
- package/dist/ui/{chunk-AD7RBRD3.js → chunk-WJLIYGWJ.js} +5 -5
- package/dist/ui/index.html +2 -2
- package/dist/ui/{main-TZL26MZU.js → main-LGW7AYEA.js} +2 -2
- package/dist/ui/{styles-IKG3B6AM.css → styles-CDN434T2.css} +1 -1
- package/package.json +10 -7
- package/dist/ui/chunk-BGDH7CDV.js +0 -1
- package/dist/ui/chunk-H2J55DNK.js +0 -7
- package/dist/ui/chunk-Q7L6LLAK.js +0 -1
- package/dist/ui/chunk-VH5GRUT7.js +0 -255
- package/dist/ui/chunk-XCUOAV77.js +0 -123
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// cli/entry.ts
|
|
2
|
-
import { existsSync as
|
|
2
|
+
import { existsSync as existsSync30 } from "fs";
|
|
3
3
|
import { Builtins, Cli as Cli2 } from "clipanion";
|
|
4
4
|
|
|
5
5
|
// kernel/adapters/in-memory-progress.ts
|
|
@@ -150,14 +150,6 @@ function makeHookDispatcher(hooks, emitter) {
|
|
|
150
150
|
}
|
|
151
151
|
const byTrigger = /* @__PURE__ */ new Map();
|
|
152
152
|
for (const hook of hooks) {
|
|
153
|
-
if (hook.mode === "probabilistic") {
|
|
154
|
-
const qualifiedId2 = qualifiedExtensionId(hook.pluginId, hook.id);
|
|
155
|
-
log.warn(
|
|
156
|
-
`Probabilistic hook ${qualifiedId2} deferred to job subsystem (future job subsystem). The hook is registered but will not dispatch in-scan.`,
|
|
157
|
-
{ hookId: qualifiedId2, mode: "probabilistic" }
|
|
158
|
-
);
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
153
|
for (const trig of hook.triggers) {
|
|
162
154
|
const bucket = byTrigger.get(trig);
|
|
163
155
|
if (bucket) bucket.push(hook);
|
|
@@ -249,7 +241,7 @@ function bucketByKind(kind, instance, bag) {
|
|
|
249
241
|
}
|
|
250
242
|
}
|
|
251
243
|
|
|
252
|
-
//
|
|
244
|
+
// plugins/claude/providers/claude/schemas/skill.schema.json
|
|
253
245
|
var skill_schema_default = {
|
|
254
246
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
255
247
|
$id: "https://skill-map.dev/providers/claude/v1/frontmatter/skill.schema.json",
|
|
@@ -263,7 +255,7 @@ var skill_schema_default = {
|
|
|
263
255
|
properties: {}
|
|
264
256
|
};
|
|
265
257
|
|
|
266
|
-
//
|
|
258
|
+
// plugins/claude/providers/claude/schemas/skill-base.schema.json
|
|
267
259
|
var skill_base_schema_default = {
|
|
268
260
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
269
261
|
$id: "https://skill-map.dev/providers/claude/v1/frontmatter/skill-base.schema.json",
|
|
@@ -342,7 +334,7 @@ var skill_base_schema_default = {
|
|
|
342
334
|
}
|
|
343
335
|
};
|
|
344
336
|
|
|
345
|
-
//
|
|
337
|
+
// plugins/claude/providers/claude/schemas/agent.schema.json
|
|
346
338
|
var agent_schema_default = {
|
|
347
339
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
348
340
|
$id: "https://skill-map.dev/providers/claude/v1/frontmatter/agent.schema.json",
|
|
@@ -423,7 +415,7 @@ var agent_schema_default = {
|
|
|
423
415
|
}
|
|
424
416
|
};
|
|
425
417
|
|
|
426
|
-
//
|
|
418
|
+
// plugins/claude/providers/claude/schemas/command.schema.json
|
|
427
419
|
var command_schema_default = {
|
|
428
420
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
429
421
|
$id: "https://skill-map.dev/providers/claude/v1/frontmatter/command.schema.json",
|
|
@@ -437,14 +429,13 @@ var command_schema_default = {
|
|
|
437
429
|
properties: {}
|
|
438
430
|
};
|
|
439
431
|
|
|
440
|
-
//
|
|
432
|
+
// plugins/claude/providers/claude/index.ts
|
|
441
433
|
var claudeProvider = {
|
|
442
434
|
id: "claude",
|
|
443
435
|
pluginId: "claude",
|
|
444
436
|
kind: "provider",
|
|
445
437
|
version: "1.0.0",
|
|
446
438
|
description: "Walks Claude Code scope conventions (.claude/{agents,commands,skills}).",
|
|
447
|
-
stability: "stable",
|
|
448
439
|
// Declarative discovery: `.md` files parsed via the kernel's
|
|
449
440
|
// `frontmatter-yaml` built-in. Equals the kernel's default but stated
|
|
450
441
|
// explicitly so the Provider doubles as a copy-paste template for
|
|
@@ -474,7 +465,6 @@ var claudeProvider = {
|
|
|
474
465
|
agent: {
|
|
475
466
|
schema: "./schemas/agent.schema.json",
|
|
476
467
|
schemaJson: agent_schema_default,
|
|
477
|
-
defaultRefreshAction: "claude/summarize-agent",
|
|
478
468
|
ui: {
|
|
479
469
|
label: "Agents",
|
|
480
470
|
color: "#3b82f6",
|
|
@@ -485,7 +475,6 @@ var claudeProvider = {
|
|
|
485
475
|
command: {
|
|
486
476
|
schema: "./schemas/command.schema.json",
|
|
487
477
|
schemaJson: command_schema_default,
|
|
488
|
-
defaultRefreshAction: "claude/summarize-command",
|
|
489
478
|
ui: {
|
|
490
479
|
label: "Commands",
|
|
491
480
|
color: "#f59e0b",
|
|
@@ -499,7 +488,6 @@ var claudeProvider = {
|
|
|
499
488
|
skill: {
|
|
500
489
|
schema: "./schemas/skill.schema.json",
|
|
501
490
|
schemaJson: skill_schema_default,
|
|
502
|
-
defaultRefreshAction: "claude/summarize-skill",
|
|
503
491
|
ui: {
|
|
504
492
|
label: "Skills",
|
|
505
493
|
color: "#10b981",
|
|
@@ -523,7 +511,7 @@ var claudeProvider = {
|
|
|
523
511
|
}
|
|
524
512
|
};
|
|
525
513
|
|
|
526
|
-
//
|
|
514
|
+
// plugins/gemini/providers/gemini/schemas/agent.schema.json
|
|
527
515
|
var agent_schema_default2 = {
|
|
528
516
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
529
517
|
$id: "https://skill-map.dev/providers/gemini/v1/frontmatter/agent.schema.json",
|
|
@@ -577,7 +565,7 @@ var agent_schema_default2 = {
|
|
|
577
565
|
}
|
|
578
566
|
};
|
|
579
567
|
|
|
580
|
-
//
|
|
568
|
+
// plugins/gemini/providers/gemini/schemas/skill.schema.json
|
|
581
569
|
var skill_schema_default2 = {
|
|
582
570
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
583
571
|
$id: "https://skill-map.dev/providers/gemini/v1/frontmatter/skill.schema.json",
|
|
@@ -591,14 +579,13 @@ var skill_schema_default2 = {
|
|
|
591
579
|
properties: {}
|
|
592
580
|
};
|
|
593
581
|
|
|
594
|
-
//
|
|
582
|
+
// plugins/gemini/providers/gemini/index.ts
|
|
595
583
|
var geminiProvider = {
|
|
596
584
|
id: "gemini",
|
|
597
585
|
pluginId: "gemini",
|
|
598
586
|
kind: "provider",
|
|
599
587
|
version: "1.0.0",
|
|
600
588
|
description: "Walks Gemini CLI scope conventions (.gemini/{agents,skills}).",
|
|
601
|
-
stability: "stable",
|
|
602
589
|
read: { extensions: [".md"], parser: "frontmatter-yaml" },
|
|
603
590
|
// Per spec § A.6, defaultRefreshAction values MUST be qualified
|
|
604
591
|
// action ids. The summarize-* actions are not yet implemented as
|
|
@@ -616,7 +603,6 @@ var geminiProvider = {
|
|
|
616
603
|
agent: {
|
|
617
604
|
schema: "./schemas/agent.schema.json",
|
|
618
605
|
schemaJson: agent_schema_default2,
|
|
619
|
-
defaultRefreshAction: "gemini/summarize-agent",
|
|
620
606
|
ui: {
|
|
621
607
|
label: "Agents",
|
|
622
608
|
color: "#3b82f6",
|
|
@@ -627,7 +613,6 @@ var geminiProvider = {
|
|
|
627
613
|
skill: {
|
|
628
614
|
schema: "./schemas/skill.schema.json",
|
|
629
615
|
schemaJson: skill_schema_default2,
|
|
630
|
-
defaultRefreshAction: "gemini/summarize-skill",
|
|
631
616
|
ui: {
|
|
632
617
|
label: "Skills",
|
|
633
618
|
color: "#10b981",
|
|
@@ -644,7 +629,7 @@ var geminiProvider = {
|
|
|
644
629
|
}
|
|
645
630
|
};
|
|
646
631
|
|
|
647
|
-
//
|
|
632
|
+
// plugins/agent-skills/providers/agent-skills/schemas/skill.schema.json
|
|
648
633
|
var skill_schema_default3 = {
|
|
649
634
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
650
635
|
$id: "https://skill-map.dev/providers/agent-skills/v1/frontmatter/skill.schema.json",
|
|
@@ -658,20 +643,18 @@ var skill_schema_default3 = {
|
|
|
658
643
|
properties: {}
|
|
659
644
|
};
|
|
660
645
|
|
|
661
|
-
//
|
|
646
|
+
// plugins/agent-skills/providers/agent-skills/index.ts
|
|
662
647
|
var agentSkillsProvider = {
|
|
663
648
|
id: "agent-skills",
|
|
664
649
|
pluginId: "agent-skills",
|
|
665
650
|
kind: "provider",
|
|
666
651
|
version: "1.0.0",
|
|
667
652
|
description: "Agent Skills open standard. Vendor-neutral path `.agents/skills/<name>/SKILL.md` (Anthropic, OpenAI, Google). See agentskills.io.",
|
|
668
|
-
stability: "stable",
|
|
669
653
|
read: { extensions: [".md"], parser: "frontmatter-yaml" },
|
|
670
654
|
kinds: {
|
|
671
655
|
skill: {
|
|
672
656
|
schema: "./schemas/skill.schema.json",
|
|
673
657
|
schemaJson: skill_schema_default3,
|
|
674
|
-
defaultRefreshAction: "agent-skills/summarize-skill",
|
|
675
658
|
ui: {
|
|
676
659
|
label: "Skills",
|
|
677
660
|
color: "#10b981",
|
|
@@ -686,7 +669,7 @@ var agentSkillsProvider = {
|
|
|
686
669
|
}
|
|
687
670
|
};
|
|
688
671
|
|
|
689
|
-
//
|
|
672
|
+
// plugins/core/providers/core-markdown/schemas/markdown.schema.json
|
|
690
673
|
var markdown_schema_default = {
|
|
691
674
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
692
675
|
$id: "https://skill-map.dev/providers/core/v1/frontmatter/markdown.schema.json",
|
|
@@ -699,14 +682,13 @@ var markdown_schema_default = {
|
|
|
699
682
|
additionalProperties: true
|
|
700
683
|
};
|
|
701
684
|
|
|
702
|
-
//
|
|
685
|
+
// plugins/core/providers/core-markdown/index.ts
|
|
703
686
|
var coreMarkdownProvider = {
|
|
704
687
|
id: "markdown",
|
|
705
688
|
pluginId: "core",
|
|
706
689
|
kind: "provider",
|
|
707
690
|
version: "1.0.0",
|
|
708
691
|
description: "Universal `.md` fallback. Claims any markdown file no vendor-specific Provider classifies.",
|
|
709
|
-
stability: "stable",
|
|
710
692
|
read: { extensions: [".md"], parser: "frontmatter-yaml" },
|
|
711
693
|
// Per spec § A.6, defaultRefreshAction values MUST be qualified
|
|
712
694
|
// action ids. The summarize-markdown action is not yet implemented
|
|
@@ -723,7 +705,6 @@ var coreMarkdownProvider = {
|
|
|
723
705
|
markdown: {
|
|
724
706
|
schema: "./schemas/markdown.schema.json",
|
|
725
707
|
schemaJson: markdown_schema_default,
|
|
726
|
-
defaultRefreshAction: "core/summarize-markdown",
|
|
727
708
|
ui: {
|
|
728
709
|
label: "Markdown",
|
|
729
710
|
color: "#5b908c",
|
|
@@ -740,7 +721,7 @@ var coreMarkdownProvider = {
|
|
|
740
721
|
}
|
|
741
722
|
};
|
|
742
723
|
|
|
743
|
-
//
|
|
724
|
+
// plugins/core/extractors/annotations/index.ts
|
|
744
725
|
var ID = "annotations";
|
|
745
726
|
var annotationsExtractor = {
|
|
746
727
|
id: ID,
|
|
@@ -748,9 +729,6 @@ var annotationsExtractor = {
|
|
|
748
729
|
kind: "extractor",
|
|
749
730
|
version: "1.0.0",
|
|
750
731
|
description: "Turns the `supersedes` and `supersededBy` entries you write in a node's `.sm` sidecar into the arrows (edges) shown between nodes in the graph.",
|
|
751
|
-
stability: "stable",
|
|
752
|
-
emitsLinkKinds: ["supersedes"],
|
|
753
|
-
defaultConfidence: "high",
|
|
754
732
|
scope: "frontmatter",
|
|
755
733
|
extract(ctx) {
|
|
756
734
|
const sourcePath = ctx.node.path;
|
|
@@ -807,53 +785,15 @@ function normalizeTrigger(source) {
|
|
|
807
785
|
return out.trim();
|
|
808
786
|
}
|
|
809
787
|
|
|
810
|
-
//
|
|
811
|
-
var ID2 = "
|
|
812
|
-
var SLASH_RE = /(?<![A-Za-z0-9_/.:?#])(\/[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)/gi;
|
|
813
|
-
var slashExtractor = {
|
|
814
|
-
id: ID2,
|
|
815
|
-
pluginId: "core",
|
|
816
|
-
kind: "extractor",
|
|
817
|
-
version: "1.0.0",
|
|
818
|
-
description: "Detects `/command` invocations in a node's body and turns each one into an arrow between nodes in the graph.",
|
|
819
|
-
stability: "stable",
|
|
820
|
-
emitsLinkKinds: ["invokes"],
|
|
821
|
-
defaultConfidence: "medium",
|
|
822
|
-
scope: "body",
|
|
823
|
-
extract(ctx) {
|
|
824
|
-
const seen = /* @__PURE__ */ new Set();
|
|
825
|
-
for (const match of ctx.body.matchAll(SLASH_RE)) {
|
|
826
|
-
const original = match[1];
|
|
827
|
-
const normalized = normalizeTrigger(original);
|
|
828
|
-
if (seen.has(normalized)) continue;
|
|
829
|
-
seen.add(normalized);
|
|
830
|
-
ctx.emitLink({
|
|
831
|
-
source: ctx.node.path,
|
|
832
|
-
target: original,
|
|
833
|
-
kind: "invokes",
|
|
834
|
-
confidence: "medium",
|
|
835
|
-
sources: [ID2],
|
|
836
|
-
trigger: {
|
|
837
|
-
originalTrigger: original,
|
|
838
|
-
normalizedTrigger: normalized
|
|
839
|
-
}
|
|
840
|
-
});
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
};
|
|
844
|
-
|
|
845
|
-
// built-in-plugins/extractors/at-directive/index.ts
|
|
846
|
-
var ID3 = "at-directive";
|
|
788
|
+
// plugins/core/extractors/at-directive/index.ts
|
|
789
|
+
var ID2 = "at-directive";
|
|
847
790
|
var AT_RE = /(?:^|[^A-Za-z0-9_@])(@[a-z0-9][a-z0-9_-]*(?:[/:][a-z0-9][a-z0-9_-]*)?)/gi;
|
|
848
791
|
var atDirectiveExtractor = {
|
|
849
|
-
id:
|
|
792
|
+
id: ID2,
|
|
850
793
|
pluginId: "core",
|
|
851
794
|
kind: "extractor",
|
|
852
795
|
version: "1.0.0",
|
|
853
796
|
description: "Detects `@agent-name` mentions in a node's body and turns each one into an arrow between nodes in the graph.",
|
|
854
|
-
stability: "stable",
|
|
855
|
-
emitsLinkKinds: ["mentions"],
|
|
856
|
-
defaultConfidence: "medium",
|
|
857
797
|
scope: "body",
|
|
858
798
|
extract(ctx) {
|
|
859
799
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -867,7 +807,7 @@ var atDirectiveExtractor = {
|
|
|
867
807
|
target: original,
|
|
868
808
|
kind: "mentions",
|
|
869
809
|
confidence: "medium",
|
|
870
|
-
sources: [
|
|
810
|
+
sources: [ID2],
|
|
871
811
|
trigger: {
|
|
872
812
|
originalTrigger: original,
|
|
873
813
|
normalizedTrigger: normalized
|
|
@@ -877,19 +817,16 @@ var atDirectiveExtractor = {
|
|
|
877
817
|
}
|
|
878
818
|
};
|
|
879
819
|
|
|
880
|
-
//
|
|
881
|
-
var
|
|
820
|
+
// plugins/core/extractors/external-url-counter/index.ts
|
|
821
|
+
var ID3 = "external-url-counter";
|
|
882
822
|
var URL_RE = /https?:\/\/[^\s<>"'`)\]]+/g;
|
|
883
823
|
var TRAILING_PUNCT = /[.,;:!?]+$/;
|
|
884
824
|
var externalUrlCounterExtractor = {
|
|
885
|
-
id:
|
|
825
|
+
id: ID3,
|
|
886
826
|
pluginId: "core",
|
|
887
827
|
kind: "extractor",
|
|
888
828
|
version: "1.0.0",
|
|
889
829
|
description: "Counts the distinct external URLs in a node's body and shows the total on the card.",
|
|
890
|
-
stability: "stable",
|
|
891
|
-
emitsLinkKinds: ["references"],
|
|
892
|
-
defaultConfidence: "low",
|
|
893
830
|
scope: "body",
|
|
894
831
|
/**
|
|
895
832
|
* Phase 6 / View contribution system, surface the distinct-URL
|
|
@@ -907,7 +844,7 @@ var externalUrlCounterExtractor = {
|
|
|
907
844
|
* inherited from the footer `.sm-gnode__stat` styles cloned by
|
|
908
845
|
* the `NodeCounter` renderer.
|
|
909
846
|
*/
|
|
910
|
-
|
|
847
|
+
ui: {
|
|
911
848
|
count: {
|
|
912
849
|
slot: "card.footer.left",
|
|
913
850
|
icon: "pi-link",
|
|
@@ -932,7 +869,7 @@ var externalUrlCounterExtractor = {
|
|
|
932
869
|
target: normalized,
|
|
933
870
|
kind: "references",
|
|
934
871
|
confidence: "low",
|
|
935
|
-
sources: [
|
|
872
|
+
sources: [ID3],
|
|
936
873
|
trigger: {
|
|
937
874
|
originalTrigger: original,
|
|
938
875
|
normalizedTrigger: normalized
|
|
@@ -977,20 +914,17 @@ function lineFor(lineStarts, offset) {
|
|
|
977
914
|
return lo + 1;
|
|
978
915
|
}
|
|
979
916
|
|
|
980
|
-
//
|
|
917
|
+
// plugins/core/extractors/markdown-link/index.ts
|
|
981
918
|
import { posix as pathPosix } from "path";
|
|
982
|
-
var
|
|
919
|
+
var ID4 = "markdown-link";
|
|
983
920
|
var LINK_RE = /(?<!!)\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
984
921
|
var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
|
|
985
922
|
var markdownLinkExtractor = {
|
|
986
|
-
id:
|
|
923
|
+
id: ID4,
|
|
987
924
|
pluginId: "core",
|
|
988
925
|
kind: "extractor",
|
|
989
926
|
version: "1.0.0",
|
|
990
927
|
description: "Detects markdown links (`[text](path)`) in a node's body and turns each one into an arrow between nodes in the graph.",
|
|
991
|
-
stability: "stable",
|
|
992
|
-
emitsLinkKinds: ["references"],
|
|
993
|
-
defaultConfidence: "high",
|
|
994
928
|
scope: "body",
|
|
995
929
|
extract(ctx) {
|
|
996
930
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1008,7 +942,7 @@ var markdownLinkExtractor = {
|
|
|
1008
942
|
target: resolved,
|
|
1009
943
|
kind: "references",
|
|
1010
944
|
confidence: "high",
|
|
1011
|
-
sources: [
|
|
945
|
+
sources: [ID4],
|
|
1012
946
|
trigger: {
|
|
1013
947
|
originalTrigger: original,
|
|
1014
948
|
normalizedTrigger: resolved
|
|
@@ -1047,99 +981,50 @@ function lineFor2(lineStarts, offset) {
|
|
|
1047
981
|
return lo + 1;
|
|
1048
982
|
}
|
|
1049
983
|
|
|
1050
|
-
//
|
|
1051
|
-
var
|
|
1052
|
-
var
|
|
1053
|
-
var
|
|
1054
|
-
|
|
1055
|
-
id: ID6,
|
|
984
|
+
// plugins/core/extractors/slash/index.ts
|
|
985
|
+
var ID5 = "slash";
|
|
986
|
+
var SLASH_RE = /(?<![A-Za-z0-9_/.:?#])(\/[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)/gi;
|
|
987
|
+
var slashExtractor = {
|
|
988
|
+
id: ID5,
|
|
1056
989
|
pluginId: "core",
|
|
1057
|
-
kind: "
|
|
990
|
+
kind: "extractor",
|
|
1058
991
|
version: "1.0.0",
|
|
1059
|
-
description: "
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
const issues = [];
|
|
1080
|
-
for (const node of ctx.nodes) {
|
|
1081
|
-
const stability = readStability(node);
|
|
1082
|
-
if (stability === "experimental") {
|
|
1083
|
-
ctx.emitContribution(node.path, "experimental", {
|
|
1084
|
-
value: 0,
|
|
1085
|
-
tooltip: EXPERIMENTAL_TOOLTIP
|
|
1086
|
-
});
|
|
1087
|
-
issues.push({
|
|
1088
|
-
analyzerId: ID6,
|
|
1089
|
-
severity: "info",
|
|
1090
|
-
nodeIds: [node.path],
|
|
1091
|
-
message: `Node '${node.path}' is marked experimental: API may change.`,
|
|
1092
|
-
data: { stability }
|
|
1093
|
-
});
|
|
1094
|
-
} else if (stability === "deprecated") {
|
|
1095
|
-
ctx.emitContribution(node.path, "deprecated", {
|
|
1096
|
-
value: 0,
|
|
1097
|
-
tooltip: DEPRECATED_TOOLTIP,
|
|
1098
|
-
severity: "warn"
|
|
1099
|
-
});
|
|
1100
|
-
issues.push({
|
|
1101
|
-
analyzerId: ID6,
|
|
1102
|
-
severity: "warn",
|
|
1103
|
-
nodeIds: [node.path],
|
|
1104
|
-
message: `Node '${node.path}' is marked deprecated: avoid in new code.`,
|
|
1105
|
-
data: { stability }
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
992
|
+
description: "Detects `/command` invocations in a node's body and turns each one into an arrow between nodes in the graph.",
|
|
993
|
+
scope: "body",
|
|
994
|
+
extract(ctx) {
|
|
995
|
+
const seen = /* @__PURE__ */ new Set();
|
|
996
|
+
for (const match of ctx.body.matchAll(SLASH_RE)) {
|
|
997
|
+
const original = match[1];
|
|
998
|
+
const normalized = normalizeTrigger(original);
|
|
999
|
+
if (seen.has(normalized)) continue;
|
|
1000
|
+
seen.add(normalized);
|
|
1001
|
+
ctx.emitLink({
|
|
1002
|
+
source: ctx.node.path,
|
|
1003
|
+
target: original,
|
|
1004
|
+
kind: "invokes",
|
|
1005
|
+
confidence: "medium",
|
|
1006
|
+
sources: [ID5],
|
|
1007
|
+
trigger: {
|
|
1008
|
+
originalTrigger: original,
|
|
1009
|
+
normalizedTrigger: normalized
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1108
1012
|
}
|
|
1109
|
-
return issues;
|
|
1110
1013
|
}
|
|
1111
1014
|
};
|
|
1112
|
-
function readStability(node) {
|
|
1113
|
-
const fromAnn = node.sidecar?.annotations?.["stability"];
|
|
1114
|
-
if (isStability(fromAnn)) return fromAnn;
|
|
1115
|
-
const legacy = readLegacyMetadataStability(node.frontmatter);
|
|
1116
|
-
return isStability(legacy) ? legacy : null;
|
|
1117
|
-
}
|
|
1118
|
-
function readLegacyMetadataStability(fm) {
|
|
1119
|
-
if (!fm) return void 0;
|
|
1120
|
-
const meta = fm["metadata"];
|
|
1121
|
-
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
|
|
1122
|
-
return meta["stability"];
|
|
1123
|
-
}
|
|
1124
|
-
function isStability(value) {
|
|
1125
|
-
return value === "experimental" || value === "deprecated" || value === "stable";
|
|
1126
|
-
}
|
|
1127
1015
|
|
|
1128
|
-
//
|
|
1129
|
-
var
|
|
1016
|
+
// plugins/core/extractors/tools-count/index.ts
|
|
1017
|
+
var ID6 = "tools-count";
|
|
1130
1018
|
var TOOLTIP_MAX = 255;
|
|
1131
1019
|
var toolsCountExtractor = {
|
|
1132
|
-
id:
|
|
1020
|
+
id: ID6,
|
|
1133
1021
|
pluginId: "core",
|
|
1134
1022
|
kind: "extractor",
|
|
1135
1023
|
version: "1.0.0",
|
|
1136
1024
|
description: "Counts the tools an agent declares in its frontmatter and shows the total on the agent card.",
|
|
1137
|
-
stability: "stable",
|
|
1138
|
-
emitsLinkKinds: [],
|
|
1139
|
-
defaultConfidence: "high",
|
|
1140
1025
|
scope: "frontmatter",
|
|
1141
|
-
|
|
1142
|
-
|
|
1026
|
+
precondition: { kind: ["claude/agent"] },
|
|
1027
|
+
ui: {
|
|
1143
1028
|
count: {
|
|
1144
1029
|
slot: "card.footer.left",
|
|
1145
1030
|
icon: "pi-wrench",
|
|
@@ -1168,155 +1053,132 @@ function buildTooltip(names) {
|
|
|
1168
1053
|
return `${joined.slice(0, TOOLTIP_MAX - 1)}\u2026`;
|
|
1169
1054
|
}
|
|
1170
1055
|
|
|
1171
|
-
//
|
|
1172
|
-
var
|
|
1173
|
-
/**
|
|
1174
|
-
|
|
1175
|
-
* cause part. Used for the advertiser-ambiguous-only, invocation-
|
|
1176
|
-
* ambiguous-only, and cross-kind-only branches.
|
|
1177
|
-
*/
|
|
1178
|
-
messageOnePart: 'Trigger "{{normalized}}" has {{part}}.',
|
|
1179
|
-
/**
|
|
1180
|
-
* Top-level message when `analyzeTriggerBucket` accumulated two cause
|
|
1181
|
-
* parts (advertiser-ambiguous AND invocation-ambiguous fire together).
|
|
1182
|
-
* The joiner lives inside the template so future locales can adapt it
|
|
1183
|
-
* (e.g. `'; y '` in Spanish) without touching the rule code.
|
|
1184
|
-
*/
|
|
1185
|
-
messageTwoParts: 'Trigger "{{normalized}}" has {{first}}; and {{second}}.',
|
|
1186
|
-
/** `<n> nodes advertise it: <list>` part, fires on the advertiser-ambiguous branch. */
|
|
1187
|
-
partAdvertisers: "{{count}} nodes advertise it: {{paths}}",
|
|
1188
|
-
/** `<n> distinct invocation forms: <list>` part, fires on the invocation-ambiguous branch. */
|
|
1189
|
-
partInvocations: "{{count}} distinct invocation forms: {{forms}}",
|
|
1190
|
-
/** Singular cross-kind cause: `non-canonical invocation <form> against advertiser <path>`. */
|
|
1191
|
-
partNonCanonicalSingular: "non-canonical invocation {{forms}} against advertiser {{advertiser}}",
|
|
1192
|
-
/** Plural cross-kind cause: `non-canonical invocations <forms> against advertiser <path>`. */
|
|
1193
|
-
partNonCanonicalPlural: "non-canonical invocations {{forms}} against advertiser {{advertiser}}"
|
|
1056
|
+
// plugins/core/analyzers/annotation-orphan/text.ts
|
|
1057
|
+
var ANNOTATION_ORPHAN_TEXTS = {
|
|
1058
|
+
/** Sidecar `<path>.sm` has no matching `<path>.md`. */
|
|
1059
|
+
message: "Orphan sidecar: {{sidecarPath}} has no matching markdown node at {{expectedMdPath}}."
|
|
1194
1060
|
};
|
|
1195
1061
|
|
|
1196
|
-
//
|
|
1197
|
-
var
|
|
1198
|
-
var
|
|
1199
|
-
|
|
1200
|
-
"skill",
|
|
1201
|
-
"agent"
|
|
1202
|
-
]);
|
|
1203
|
-
var triggerCollisionAnalyzer = {
|
|
1204
|
-
id: ID8,
|
|
1062
|
+
// plugins/core/analyzers/annotation-orphan/index.ts
|
|
1063
|
+
var ID7 = "annotation-orphan";
|
|
1064
|
+
var annotationOrphanAnalyzer = {
|
|
1065
|
+
id: ID7,
|
|
1205
1066
|
pluginId: "core",
|
|
1206
1067
|
kind: "analyzer",
|
|
1207
|
-
mode: "deterministic",
|
|
1208
1068
|
version: "1.0.0",
|
|
1209
|
-
description: "Detects and flags
|
|
1210
|
-
|
|
1211
|
-
// Two claim-collection passes (advertisement + invocation) feeding
|
|
1212
|
-
// the bucket map. Per-bucket analysis lives in `analyzeTriggerBucket`.
|
|
1213
|
-
// eslint-disable-next-line complexity
|
|
1069
|
+
description: "Detects and flags sidecars (`.sm`) whose `.md` no longer exists.",
|
|
1070
|
+
mode: "deterministic",
|
|
1214
1071
|
evaluate(ctx) {
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
const bucket = buckets.get(key) ?? [];
|
|
1218
|
-
bucket.push(claim);
|
|
1219
|
-
buckets.set(key, bucket);
|
|
1220
|
-
};
|
|
1221
|
-
for (const node of ctx.nodes) {
|
|
1222
|
-
if (!ADVERTISING_KINDS.has(node.kind)) continue;
|
|
1223
|
-
const raw = node.frontmatter?.["name"];
|
|
1224
|
-
if (typeof raw !== "string" || raw.length === 0) continue;
|
|
1225
|
-
const normalized = `/${normalizeTrigger(raw)}`;
|
|
1226
|
-
if (normalized === "/") continue;
|
|
1227
|
-
push(normalized, {
|
|
1228
|
-
kind: "advertiser",
|
|
1229
|
-
token: node.path,
|
|
1230
|
-
nodeId: node.path,
|
|
1231
|
-
canonicalForm: `/${raw}`
|
|
1232
|
-
});
|
|
1233
|
-
}
|
|
1234
|
-
for (const link2 of ctx.links) {
|
|
1235
|
-
const normalized = link2.trigger?.normalizedTrigger;
|
|
1236
|
-
if (!normalized) continue;
|
|
1237
|
-
push(normalized, {
|
|
1238
|
-
kind: "invocation",
|
|
1239
|
-
token: link2.target,
|
|
1240
|
-
nodeId: link2.source
|
|
1241
|
-
});
|
|
1242
|
-
}
|
|
1072
|
+
const orphans = ctx.orphanSidecars;
|
|
1073
|
+
if (!orphans || orphans.length === 0) return [];
|
|
1243
1074
|
const issues = [];
|
|
1244
|
-
for (const
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1075
|
+
for (const orphan of orphans) {
|
|
1076
|
+
const expectedMdRelative = orphan.relativePath.endsWith(".sm") ? `${orphan.relativePath.slice(0, -".sm".length)}.md` : `${orphan.relativePath}.md`;
|
|
1077
|
+
issues.push({
|
|
1078
|
+
analyzerId: ID7,
|
|
1079
|
+
severity: "warn",
|
|
1080
|
+
nodeIds: [expectedMdRelative],
|
|
1081
|
+
message: tx(ANNOTATION_ORPHAN_TEXTS.message, {
|
|
1082
|
+
sidecarPath: orphan.relativePath,
|
|
1083
|
+
expectedMdPath: orphan.expectedMdPath
|
|
1084
|
+
}),
|
|
1085
|
+
data: {
|
|
1086
|
+
sidecarPath: orphan.relativePath,
|
|
1087
|
+
expectedMdPath: orphan.expectedMdPath
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1247
1090
|
}
|
|
1248
1091
|
return issues;
|
|
1249
1092
|
}
|
|
1250
1093
|
};
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1094
|
+
|
|
1095
|
+
// plugins/core/analyzers/annotation-stale/text.ts
|
|
1096
|
+
var ANNOTATION_STALE_TEXTS = {
|
|
1097
|
+
/** body changed since last bump */
|
|
1098
|
+
bodyDrift: "{{path}}: sidecar `.sm` is stale (body changed since last bump).",
|
|
1099
|
+
/** frontmatter changed since last bump */
|
|
1100
|
+
frontmatterDrift: "{{path}}: sidecar `.sm` is stale (frontmatter changed since last bump).",
|
|
1101
|
+
/** both body and frontmatter changed */
|
|
1102
|
+
bothDrift: "{{path}}: sidecar `.sm` is stale (body and frontmatter changed since last bump).",
|
|
1103
|
+
// Tooltips for the `card.footer.right` clock chip emitted alongside
|
|
1104
|
+
// the issue. Lists only the drifted face(s), in-sync faces are
|
|
1105
|
+
// omitted so the operator immediately sees what's modified without
|
|
1106
|
+
// scanning prose. No `{{path}}` placeholder, the chip already sits
|
|
1107
|
+
// on the affected node. The hint `sm bump <path>` keeps `<path>` as
|
|
1108
|
+
// a literal placeholder the operator substitutes.
|
|
1109
|
+
bodyTooltip: "Sidecar drift since last bump:\n \u2022 body\nRun `sm bump <path>` to refresh.",
|
|
1110
|
+
frontmatterTooltip: "Sidecar drift since last bump:\n \u2022 frontmatter\nRun `sm bump <path>` to refresh.",
|
|
1111
|
+
bothTooltip: "Sidecar drift since last bump:\n \u2022 body\n \u2022 frontmatter\nRun `sm bump <path>` to refresh."
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
// plugins/core/analyzers/annotation-stale/index.ts
|
|
1115
|
+
var ID8 = "annotation-stale";
|
|
1116
|
+
var annotationStaleAnalyzer = {
|
|
1117
|
+
id: ID8,
|
|
1118
|
+
pluginId: "core",
|
|
1119
|
+
kind: "analyzer",
|
|
1120
|
+
version: "1.0.0",
|
|
1121
|
+
description: "Detects and marks sidecars (`.sm`) out of date of their `.md`.",
|
|
1122
|
+
mode: "deterministic",
|
|
1123
|
+
// The natural fix is to bump the node: refreshes `for` hashes,
|
|
1124
|
+
// increments `annotations.version`, and stamps the audit block. The
|
|
1125
|
+
// UI surfaces `core/bump` in the node inspector under "Recommended
|
|
1126
|
+
// for issues" whenever this analyzer fires.
|
|
1127
|
+
ui: {
|
|
1128
|
+
// A `pi-clock` chip in the footer-right cluster so the operator
|
|
1129
|
+
// spots drift in the list / inspector view (and on the graph card
|
|
1130
|
+
// body). Emitted with `value: 0` and `emitWhenEmpty: true` so the
|
|
1131
|
+
// renderer treats it as icon-only, drift severity is binary at
|
|
1132
|
+
// this surface (the tooltip carries the per-face detail body /
|
|
1133
|
+
// frontmatter / both). The corner badge on `graph.node.alert` was
|
|
1134
|
+
// dropped on purpose: a tooltip on the footer chip is enough, and
|
|
1135
|
+
// the corner badge stacked on top of broken-ref / unknown-field
|
|
1136
|
+
// alerts produced visual noise.
|
|
1137
|
+
staleIcon: {
|
|
1138
|
+
slot: "card.footer.right",
|
|
1139
|
+
icon: "pi-clock",
|
|
1140
|
+
emitWhenEmpty: true,
|
|
1141
|
+
priority: 20
|
|
1142
|
+
}
|
|
1143
|
+
},
|
|
1144
|
+
evaluate(ctx) {
|
|
1145
|
+
const issues = [];
|
|
1146
|
+
for (const node of ctx.nodes) {
|
|
1147
|
+
const status = node.sidecar?.status;
|
|
1148
|
+
if (status === void 0 || status === null) continue;
|
|
1149
|
+
if (status === "fresh") continue;
|
|
1150
|
+
const message = status === "stale-body" ? tx(ANNOTATION_STALE_TEXTS.bodyDrift, { path: node.path }) : status === "stale-frontmatter" ? tx(ANNOTATION_STALE_TEXTS.frontmatterDrift, { path: node.path }) : tx(ANNOTATION_STALE_TEXTS.bothDrift, { path: node.path });
|
|
1151
|
+
issues.push({
|
|
1152
|
+
analyzerId: ID8,
|
|
1153
|
+
severity: "warn",
|
|
1154
|
+
nodeIds: [node.path],
|
|
1155
|
+
message,
|
|
1156
|
+
data: { status }
|
|
1157
|
+
});
|
|
1158
|
+
ctx.emitContribution(node.path, "staleIcon", {
|
|
1159
|
+
value: 0,
|
|
1160
|
+
severity: "warn",
|
|
1161
|
+
tooltip: tooltipFor(status)
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
return issues;
|
|
1278
1165
|
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
parts.push(
|
|
1289
|
-
tx(template, {
|
|
1290
|
-
forms: nonCanonicalInvocations.join(", "),
|
|
1291
|
-
advertiser: advertiserPaths[0]
|
|
1292
|
-
})
|
|
1293
|
-
);
|
|
1166
|
+
};
|
|
1167
|
+
function tooltipFor(status) {
|
|
1168
|
+
switch (status) {
|
|
1169
|
+
case "stale-body":
|
|
1170
|
+
return ANNOTATION_STALE_TEXTS.bodyTooltip;
|
|
1171
|
+
case "stale-frontmatter":
|
|
1172
|
+
return ANNOTATION_STALE_TEXTS.frontmatterTooltip;
|
|
1173
|
+
case "stale-both":
|
|
1174
|
+
return ANNOTATION_STALE_TEXTS.bothTooltip;
|
|
1294
1175
|
}
|
|
1295
|
-
const message = parts.length === 2 ? tx(TRIGGER_COLLISION_TEXTS.messageTwoParts, {
|
|
1296
|
-
normalized,
|
|
1297
|
-
first: parts[0],
|
|
1298
|
-
second: parts[1]
|
|
1299
|
-
}) : tx(TRIGGER_COLLISION_TEXTS.messageOnePart, {
|
|
1300
|
-
normalized,
|
|
1301
|
-
part: parts[0]
|
|
1302
|
-
});
|
|
1303
|
-
return {
|
|
1304
|
-
analyzerId: ID8,
|
|
1305
|
-
severity: "error",
|
|
1306
|
-
nodeIds,
|
|
1307
|
-
message,
|
|
1308
|
-
data: {
|
|
1309
|
-
normalizedTrigger: normalized,
|
|
1310
|
-
invocationTargets,
|
|
1311
|
-
advertiserPaths
|
|
1312
|
-
}
|
|
1313
|
-
};
|
|
1314
1176
|
}
|
|
1315
1177
|
|
|
1316
|
-
//
|
|
1178
|
+
// plugins/core/analyzers/broken-ref/index.ts
|
|
1317
1179
|
import { resolve } from "path";
|
|
1318
1180
|
|
|
1319
|
-
//
|
|
1181
|
+
// plugins/core/analyzers/broken-ref/text.ts
|
|
1320
1182
|
var BROKEN_REF_TEXTS = {
|
|
1321
1183
|
/** `Broken <kind> reference from <source> → <target>` */
|
|
1322
1184
|
message: "Broken {{kind}} reference from {{source}} \u2192 {{target}}",
|
|
@@ -1326,7 +1188,7 @@ var BROKEN_REF_TEXTS = {
|
|
|
1326
1188
|
alertTooltipMany: "This node has {{count}} broken references. Open the inspector for details."
|
|
1327
1189
|
};
|
|
1328
1190
|
|
|
1329
|
-
//
|
|
1191
|
+
// plugins/core/analyzers/broken-ref/index.ts
|
|
1330
1192
|
var ID9 = "broken-ref";
|
|
1331
1193
|
var brokenRefAnalyzer = {
|
|
1332
1194
|
id: ID9,
|
|
@@ -1334,9 +1196,8 @@ var brokenRefAnalyzer = {
|
|
|
1334
1196
|
kind: "analyzer",
|
|
1335
1197
|
version: "1.0.0",
|
|
1336
1198
|
description: "Detects and flags arrows pointing at a node not part of the current scan.",
|
|
1337
|
-
stability: "stable",
|
|
1338
1199
|
mode: "deterministic",
|
|
1339
|
-
|
|
1200
|
+
ui: {
|
|
1340
1201
|
// Corner badge on the graph card; count omitted when there is a
|
|
1341
1202
|
// single broken ref (avoids a noisy "icon + 1" chip).
|
|
1342
1203
|
alert: {
|
|
@@ -1439,66 +1300,69 @@ function isPathStyleLink(link2) {
|
|
|
1439
1300
|
return true;
|
|
1440
1301
|
}
|
|
1441
1302
|
|
|
1442
|
-
//
|
|
1443
|
-
var
|
|
1444
|
-
|
|
1445
|
-
|
|
1303
|
+
// plugins/core/analyzers/contribution-orphan/index.ts
|
|
1304
|
+
var ID10 = "contribution-orphan";
|
|
1305
|
+
var contributionOrphanAnalyzer = {
|
|
1306
|
+
id: ID10,
|
|
1307
|
+
pluginId: "core",
|
|
1308
|
+
kind: "analyzer",
|
|
1309
|
+
version: "0.0.0",
|
|
1310
|
+
description: "Detects and warns about plugin data referencing nodes renamed or deleted in the latest scan.",
|
|
1311
|
+
mode: "deterministic",
|
|
1312
|
+
evaluate(_ctx) {
|
|
1313
|
+
return [];
|
|
1314
|
+
}
|
|
1446
1315
|
};
|
|
1447
1316
|
|
|
1448
|
-
//
|
|
1449
|
-
var
|
|
1450
|
-
|
|
1451
|
-
|
|
1317
|
+
// plugins/core/analyzers/job-orphan-file/text.ts
|
|
1318
|
+
var JOB_ORPHAN_FILE_TEXTS = {
|
|
1319
|
+
/**
|
|
1320
|
+
* `<path>.md` lives under `.skill-map/jobs/` but no `state_jobs.filePath`
|
|
1321
|
+
* row references it. Run `sm job prune --orphan-files` to remove.
|
|
1322
|
+
*/
|
|
1323
|
+
message: "Orphan job file: {{filePath}} is not referenced by any state_jobs row. Run `sm job prune --orphan-files` to remove it."
|
|
1324
|
+
};
|
|
1325
|
+
|
|
1326
|
+
// plugins/core/analyzers/job-orphan-file/index.ts
|
|
1327
|
+
var ID11 = "job-orphan-file";
|
|
1328
|
+
var jobOrphanFileAnalyzer = {
|
|
1329
|
+
id: ID11,
|
|
1452
1330
|
pluginId: "core",
|
|
1453
1331
|
kind: "analyzer",
|
|
1454
1332
|
version: "1.0.0",
|
|
1455
|
-
description: "Detects and
|
|
1456
|
-
stability: "stable",
|
|
1333
|
+
description: "Detects and flags leftover job result files (no live job references them). Cleanup via `sm job prune --orphan-files`.",
|
|
1457
1334
|
mode: "deterministic",
|
|
1458
1335
|
evaluate(ctx) {
|
|
1336
|
+
const orphans = ctx.orphanJobFiles;
|
|
1337
|
+
if (!orphans || orphans.length === 0) return [];
|
|
1459
1338
|
const issues = [];
|
|
1460
|
-
for (const
|
|
1461
|
-
const supersededBy = pickSupersededBy(node);
|
|
1462
|
-
if (supersededBy === null) continue;
|
|
1339
|
+
for (const filePath of orphans) {
|
|
1463
1340
|
issues.push({
|
|
1464
|
-
analyzerId:
|
|
1465
|
-
severity: "
|
|
1466
|
-
nodeIds: [
|
|
1467
|
-
message: tx(
|
|
1468
|
-
|
|
1469
|
-
supersededBy
|
|
1470
|
-
}),
|
|
1471
|
-
data: { supersededBy }
|
|
1341
|
+
analyzerId: ID11,
|
|
1342
|
+
severity: "warn",
|
|
1343
|
+
nodeIds: [filePath],
|
|
1344
|
+
message: tx(JOB_ORPHAN_FILE_TEXTS.message, { filePath }),
|
|
1345
|
+
data: { filePath }
|
|
1472
1346
|
});
|
|
1473
1347
|
}
|
|
1474
1348
|
return issues;
|
|
1475
1349
|
}
|
|
1476
1350
|
};
|
|
1477
|
-
function pickSupersededBy(node) {
|
|
1478
|
-
const sidecar = node.sidecar;
|
|
1479
|
-
if (!sidecar || sidecar.present !== true) return null;
|
|
1480
|
-
const ann = sidecar.annotations;
|
|
1481
|
-
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return null;
|
|
1482
|
-
const value = ann["supersededBy"];
|
|
1483
|
-
if (typeof value !== "string" || value.length === 0) return null;
|
|
1484
|
-
return value;
|
|
1485
|
-
}
|
|
1486
1351
|
|
|
1487
|
-
//
|
|
1352
|
+
// plugins/core/analyzers/link-conflict/text.ts
|
|
1488
1353
|
var LINK_CONFLICT_TEXTS = {
|
|
1489
1354
|
/** `Detectors disagree on link kind for <source> → <target> (<kindList>)` */
|
|
1490
1355
|
message: "Detectors disagree on link kind for {{source}} \u2192 {{target}} ({{kindList}})"
|
|
1491
1356
|
};
|
|
1492
1357
|
|
|
1493
|
-
//
|
|
1494
|
-
var
|
|
1358
|
+
// plugins/core/analyzers/link-conflict/index.ts
|
|
1359
|
+
var ID12 = "link-conflict";
|
|
1495
1360
|
var linkConflictAnalyzer = {
|
|
1496
|
-
id:
|
|
1361
|
+
id: ID12,
|
|
1497
1362
|
pluginId: "core",
|
|
1498
1363
|
kind: "analyzer",
|
|
1499
1364
|
version: "1.0.0",
|
|
1500
1365
|
description: 'Detects and flags conflicting arrow meanings between extractors (e.g. "references" vs "invokes").',
|
|
1501
|
-
stability: "stable",
|
|
1502
1366
|
mode: "deterministic",
|
|
1503
1367
|
// Bucket links by (source, target), then per-bucket detect distinct
|
|
1504
1368
|
// kinds. The branching is intrinsic to the per-bucket conflict
|
|
@@ -1542,7 +1406,7 @@ var linkConflictAnalyzer = {
|
|
|
1542
1406
|
const [source, target] = key.split("\0");
|
|
1543
1407
|
const kindList = variants.map((v) => v.kind).join(" / ");
|
|
1544
1408
|
issues.push({
|
|
1545
|
-
analyzerId:
|
|
1409
|
+
analyzerId: ID12,
|
|
1546
1410
|
severity: "warn",
|
|
1547
1411
|
nodeIds: [source, target],
|
|
1548
1412
|
message: tx(LINK_CONFLICT_TEXTS.message, {
|
|
@@ -1567,168 +1431,386 @@ function rankConfidence(c) {
|
|
|
1567
1431
|
}
|
|
1568
1432
|
}
|
|
1569
1433
|
|
|
1570
|
-
//
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1434
|
+
// kernel/util/trigger-resolve.ts
|
|
1435
|
+
function buildNameIndex(nodes) {
|
|
1436
|
+
const out = /* @__PURE__ */ new Map();
|
|
1437
|
+
indexByCanonicalName(nodes, out);
|
|
1438
|
+
fillIndexWithPathBasename(nodes, out);
|
|
1439
|
+
return out;
|
|
1440
|
+
}
|
|
1441
|
+
function indexByCanonicalName(nodes, out) {
|
|
1442
|
+
for (const node of nodes) {
|
|
1443
|
+
const raw = canonicalName(node);
|
|
1444
|
+
if (raw === null) continue;
|
|
1445
|
+
const key = normalizeTrigger(raw);
|
|
1446
|
+
if (!out.has(key)) out.set(key, node.path);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
function fillIndexWithPathBasename(nodes, out) {
|
|
1450
|
+
for (const node of nodes) {
|
|
1451
|
+
if (canonicalName(node) !== null) continue;
|
|
1452
|
+
const derived = pathBasenameForLink(node.path);
|
|
1453
|
+
if (derived.length === 0) continue;
|
|
1454
|
+
const key = normalizeTrigger(derived);
|
|
1455
|
+
if (!out.has(key)) out.set(key, node.path);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
function canonicalName(node) {
|
|
1459
|
+
const raw = node.frontmatter?.["name"];
|
|
1460
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
1461
|
+
return raw;
|
|
1462
|
+
}
|
|
1463
|
+
function pathBasenameForLink(path) {
|
|
1464
|
+
const segments = path.split("/").filter((s) => s.length > 0);
|
|
1465
|
+
if (segments.length === 0) return path;
|
|
1466
|
+
const last = segments[segments.length - 1];
|
|
1467
|
+
if (last === "SKILL.md" && segments.length >= 2) {
|
|
1468
|
+
return segments[segments.length - 2];
|
|
1469
|
+
}
|
|
1470
|
+
return last.replace(/\.md$/, "");
|
|
1471
|
+
}
|
|
1472
|
+
function resolveLinkTargetToPath(link2, nameIndex) {
|
|
1473
|
+
const raw = link2.target;
|
|
1474
|
+
const sigil = raw.charAt(0);
|
|
1475
|
+
if (sigil !== "/" && sigil !== "@") return raw;
|
|
1476
|
+
const normalizedTrigger = link2.trigger?.normalizedTrigger;
|
|
1477
|
+
const normalized = typeof normalizedTrigger === "string" ? normalizedTrigger.replace(/^[/@]/, "").trim() : normalizeTrigger(raw.slice(1));
|
|
1478
|
+
const resolved = nameIndex.get(normalized);
|
|
1479
|
+
return resolved ?? raw;
|
|
1480
|
+
}
|
|
1588
1481
|
|
|
1589
|
-
//
|
|
1590
|
-
var
|
|
1591
|
-
var
|
|
1592
|
-
id:
|
|
1482
|
+
// plugins/core/analyzers/link-counts/index.ts
|
|
1483
|
+
var ID13 = "link-counts";
|
|
1484
|
+
var linkCountsAnalyzer = {
|
|
1485
|
+
id: ID13,
|
|
1593
1486
|
pluginId: "core",
|
|
1594
1487
|
kind: "analyzer",
|
|
1595
1488
|
version: "1.0.0",
|
|
1596
|
-
description: "
|
|
1597
|
-
stability: "stable",
|
|
1489
|
+
description: "Counts incoming and outgoing links per node.",
|
|
1598
1490
|
mode: "deterministic",
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
// the corner badge stacked on top of broken-ref / unknown-field
|
|
1613
|
-
// alerts produced visual noise.
|
|
1614
|
-
staleIcon: {
|
|
1615
|
-
slot: "card.footer.right",
|
|
1616
|
-
icon: "pi-clock",
|
|
1617
|
-
emitWhenEmpty: true,
|
|
1491
|
+
ui: {
|
|
1492
|
+
linksIn: {
|
|
1493
|
+
slot: "card.footer.left",
|
|
1494
|
+
icon: "pi-download",
|
|
1495
|
+
label: "incoming links",
|
|
1496
|
+
emitWhenEmpty: false,
|
|
1497
|
+
priority: 10
|
|
1498
|
+
},
|
|
1499
|
+
linksOut: {
|
|
1500
|
+
slot: "card.footer.left",
|
|
1501
|
+
icon: "pi-upload",
|
|
1502
|
+
label: "outgoing links",
|
|
1503
|
+
emitWhenEmpty: false,
|
|
1618
1504
|
priority: 20
|
|
1619
1505
|
}
|
|
1620
1506
|
},
|
|
1507
|
+
evaluate(ctx) {
|
|
1508
|
+
const nameIndex = buildNameIndex(ctx.nodes);
|
|
1509
|
+
const perTarget = /* @__PURE__ */ new Map();
|
|
1510
|
+
const perSource = /* @__PURE__ */ new Map();
|
|
1511
|
+
for (const link2 of ctx.links) {
|
|
1512
|
+
const resolvedTarget = resolveLinkTargetToPath(link2, nameIndex);
|
|
1513
|
+
bump(perTarget, resolvedTarget, link2.kind);
|
|
1514
|
+
bump(perSource, link2.source, link2.kind);
|
|
1515
|
+
}
|
|
1516
|
+
for (const node of ctx.nodes) {
|
|
1517
|
+
emitChip(ctx, node.path, "linksIn", perTarget.get(node.path));
|
|
1518
|
+
emitChip(ctx, node.path, "linksOut", perSource.get(node.path));
|
|
1519
|
+
}
|
|
1520
|
+
return [];
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
function bump(map, key, kind) {
|
|
1524
|
+
let byKind = map.get(key);
|
|
1525
|
+
if (!byKind) {
|
|
1526
|
+
byKind = /* @__PURE__ */ new Map();
|
|
1527
|
+
map.set(key, byKind);
|
|
1528
|
+
}
|
|
1529
|
+
byKind.set(kind, (byKind.get(kind) ?? 0) + 1);
|
|
1530
|
+
}
|
|
1531
|
+
function emitChip(ctx, nodePath, contributionId, byKind) {
|
|
1532
|
+
if (!byKind) return;
|
|
1533
|
+
let total = 0;
|
|
1534
|
+
for (const n of byKind.values()) total += n;
|
|
1535
|
+
if (total === 0) return;
|
|
1536
|
+
const capped = Math.min(total, 99);
|
|
1537
|
+
const direction = contributionId === "linksIn" ? "in" : "out";
|
|
1538
|
+
ctx.emitContribution(nodePath, contributionId, {
|
|
1539
|
+
value: capped,
|
|
1540
|
+
tooltip: formatBreakdown(byKind, direction)
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
function formatBreakdown(byKind, direction) {
|
|
1544
|
+
const lines = [...byKind.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([kind, n]) => `${kind}: ${n}`);
|
|
1545
|
+
return [direction, ...lines].join("\n");
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// plugins/core/analyzers/stability/index.ts
|
|
1549
|
+
var ID14 = "stability";
|
|
1550
|
+
var EXPERIMENTAL_TOOLTIP = "Experimental: API may change";
|
|
1551
|
+
var DEPRECATED_TOOLTIP = "Deprecated: avoid in new code";
|
|
1552
|
+
var stabilityAnalyzer = {
|
|
1553
|
+
id: ID14,
|
|
1554
|
+
pluginId: "core",
|
|
1555
|
+
kind: "analyzer",
|
|
1556
|
+
version: "1.0.0",
|
|
1557
|
+
description: "Reports node lifecycle stage (`experimental`, `deprecated`) on the card.",
|
|
1558
|
+
mode: "deterministic",
|
|
1559
|
+
ui: {
|
|
1560
|
+
experimental: {
|
|
1561
|
+
slot: "card.footer.right",
|
|
1562
|
+
icon: "fa-solid fa-flask",
|
|
1563
|
+
label: "experimental",
|
|
1564
|
+
emitWhenEmpty: false,
|
|
1565
|
+
priority: 10
|
|
1566
|
+
},
|
|
1567
|
+
deprecated: {
|
|
1568
|
+
slot: "card.footer.right",
|
|
1569
|
+
icon: "pi-ban",
|
|
1570
|
+
label: "deprecated",
|
|
1571
|
+
emitWhenEmpty: false,
|
|
1572
|
+
priority: 10
|
|
1573
|
+
}
|
|
1574
|
+
},
|
|
1621
1575
|
evaluate(ctx) {
|
|
1622
1576
|
const issues = [];
|
|
1623
1577
|
for (const node of ctx.nodes) {
|
|
1624
|
-
const
|
|
1625
|
-
if (
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1578
|
+
const stability = readStability(node);
|
|
1579
|
+
if (stability === "experimental") {
|
|
1580
|
+
ctx.emitContribution(node.path, "experimental", {
|
|
1581
|
+
value: 0,
|
|
1582
|
+
tooltip: EXPERIMENTAL_TOOLTIP
|
|
1583
|
+
});
|
|
1584
|
+
issues.push({
|
|
1585
|
+
analyzerId: ID14,
|
|
1586
|
+
severity: "info",
|
|
1587
|
+
nodeIds: [node.path],
|
|
1588
|
+
message: `Node '${node.path}' is marked experimental: API may change.`,
|
|
1589
|
+
data: { stability }
|
|
1590
|
+
});
|
|
1591
|
+
} else if (stability === "deprecated") {
|
|
1592
|
+
ctx.emitContribution(node.path, "deprecated", {
|
|
1593
|
+
value: 0,
|
|
1594
|
+
tooltip: DEPRECATED_TOOLTIP,
|
|
1595
|
+
severity: "warn"
|
|
1596
|
+
});
|
|
1597
|
+
issues.push({
|
|
1598
|
+
analyzerId: ID14,
|
|
1599
|
+
severity: "warn",
|
|
1600
|
+
nodeIds: [node.path],
|
|
1601
|
+
message: `Node '${node.path}' is marked deprecated: avoid in new code.`,
|
|
1602
|
+
data: { stability }
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1640
1605
|
}
|
|
1641
1606
|
return issues;
|
|
1642
1607
|
}
|
|
1643
1608
|
};
|
|
1644
|
-
function
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1609
|
+
function readStability(node) {
|
|
1610
|
+
const fromAnn = node.sidecar?.annotations?.["stability"];
|
|
1611
|
+
if (isStability(fromAnn)) return fromAnn;
|
|
1612
|
+
const legacy = readLegacyMetadataStability(node.frontmatter);
|
|
1613
|
+
return isStability(legacy) ? legacy : null;
|
|
1614
|
+
}
|
|
1615
|
+
function readLegacyMetadataStability(fm) {
|
|
1616
|
+
if (!fm) return void 0;
|
|
1617
|
+
const meta = fm["metadata"];
|
|
1618
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
|
|
1619
|
+
return meta["stability"];
|
|
1620
|
+
}
|
|
1621
|
+
function isStability(value) {
|
|
1622
|
+
return value === "experimental" || value === "deprecated" || value === "stable";
|
|
1653
1623
|
}
|
|
1654
1624
|
|
|
1655
|
-
//
|
|
1656
|
-
var
|
|
1657
|
-
/**
|
|
1658
|
-
message: "
|
|
1625
|
+
// plugins/core/analyzers/superseded/text.ts
|
|
1626
|
+
var SUPERSEDED_TEXTS = {
|
|
1627
|
+
/** `<path> is superseded by <supersededBy>` */
|
|
1628
|
+
message: "{{path}} is superseded by {{supersededBy}}"
|
|
1659
1629
|
};
|
|
1660
1630
|
|
|
1661
|
-
//
|
|
1662
|
-
var
|
|
1663
|
-
var
|
|
1664
|
-
id:
|
|
1631
|
+
// plugins/core/analyzers/superseded/index.ts
|
|
1632
|
+
var ID15 = "superseded";
|
|
1633
|
+
var supersededAnalyzer = {
|
|
1634
|
+
id: ID15,
|
|
1665
1635
|
pluginId: "core",
|
|
1666
1636
|
kind: "analyzer",
|
|
1667
1637
|
version: "1.0.0",
|
|
1668
|
-
description: "Detects and
|
|
1669
|
-
stability: "stable",
|
|
1638
|
+
description: "Detects and marks nodes replaced by a newer one via `supersededBy`.",
|
|
1670
1639
|
mode: "deterministic",
|
|
1671
1640
|
evaluate(ctx) {
|
|
1672
|
-
const orphans = ctx.orphanSidecars;
|
|
1673
|
-
if (!orphans || orphans.length === 0) return [];
|
|
1674
1641
|
const issues = [];
|
|
1675
|
-
for (const
|
|
1676
|
-
const
|
|
1642
|
+
for (const node of ctx.nodes) {
|
|
1643
|
+
const supersededBy = pickSupersededBy(node);
|
|
1644
|
+
if (supersededBy === null) continue;
|
|
1677
1645
|
issues.push({
|
|
1678
|
-
analyzerId:
|
|
1679
|
-
severity: "
|
|
1680
|
-
nodeIds: [
|
|
1681
|
-
message: tx(
|
|
1682
|
-
|
|
1683
|
-
|
|
1646
|
+
analyzerId: ID15,
|
|
1647
|
+
severity: "info",
|
|
1648
|
+
nodeIds: [node.path],
|
|
1649
|
+
message: tx(SUPERSEDED_TEXTS.message, {
|
|
1650
|
+
path: node.path,
|
|
1651
|
+
supersededBy
|
|
1684
1652
|
}),
|
|
1685
|
-
data: {
|
|
1686
|
-
sidecarPath: orphan.relativePath,
|
|
1687
|
-
expectedMdPath: orphan.expectedMdPath
|
|
1688
|
-
}
|
|
1653
|
+
data: { supersededBy }
|
|
1689
1654
|
});
|
|
1690
1655
|
}
|
|
1691
1656
|
return issues;
|
|
1692
1657
|
}
|
|
1693
1658
|
};
|
|
1659
|
+
function pickSupersededBy(node) {
|
|
1660
|
+
const sidecar = node.sidecar;
|
|
1661
|
+
if (!sidecar || sidecar.present !== true) return null;
|
|
1662
|
+
const ann = sidecar.annotations;
|
|
1663
|
+
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return null;
|
|
1664
|
+
const value = ann["supersededBy"];
|
|
1665
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
1666
|
+
return value;
|
|
1667
|
+
}
|
|
1694
1668
|
|
|
1695
|
-
//
|
|
1696
|
-
var
|
|
1669
|
+
// plugins/core/analyzers/trigger-collision/text.ts
|
|
1670
|
+
var TRIGGER_COLLISION_TEXTS = {
|
|
1697
1671
|
/**
|
|
1698
|
-
*
|
|
1699
|
-
*
|
|
1672
|
+
* Top-level message when `analyzeTriggerBucket` accumulated exactly one
|
|
1673
|
+
* cause part. Used for the advertiser-ambiguous-only, invocation-
|
|
1674
|
+
* ambiguous-only, and cross-kind-only branches.
|
|
1700
1675
|
*/
|
|
1701
|
-
|
|
1676
|
+
messageOnePart: 'Trigger "{{normalized}}" has {{part}}.',
|
|
1677
|
+
/**
|
|
1678
|
+
* Top-level message when `analyzeTriggerBucket` accumulated two cause
|
|
1679
|
+
* parts (advertiser-ambiguous AND invocation-ambiguous fire together).
|
|
1680
|
+
* The joiner lives inside the template so future locales can adapt it
|
|
1681
|
+
* (e.g. `'; y '` in Spanish) without touching the rule code.
|
|
1682
|
+
*/
|
|
1683
|
+
messageTwoParts: 'Trigger "{{normalized}}" has {{first}}; and {{second}}.',
|
|
1684
|
+
/** `<n> nodes advertise it: <list>` part, fires on the advertiser-ambiguous branch. */
|
|
1685
|
+
partAdvertisers: "{{count}} nodes advertise it: {{paths}}",
|
|
1686
|
+
/** `<n> distinct invocation forms: <list>` part, fires on the invocation-ambiguous branch. */
|
|
1687
|
+
partInvocations: "{{count}} distinct invocation forms: {{forms}}",
|
|
1688
|
+
/** Singular cross-kind cause: `non-canonical invocation <form> against advertiser <path>`. */
|
|
1689
|
+
partNonCanonicalSingular: "non-canonical invocation {{forms}} against advertiser {{advertiser}}",
|
|
1690
|
+
/** Plural cross-kind cause: `non-canonical invocations <forms> against advertiser <path>`. */
|
|
1691
|
+
partNonCanonicalPlural: "non-canonical invocations {{forms}} against advertiser {{advertiser}}"
|
|
1702
1692
|
};
|
|
1703
1693
|
|
|
1704
|
-
//
|
|
1705
|
-
var
|
|
1706
|
-
var
|
|
1707
|
-
|
|
1694
|
+
// plugins/core/analyzers/trigger-collision/index.ts
|
|
1695
|
+
var ID16 = "trigger-collision";
|
|
1696
|
+
var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
|
|
1697
|
+
"command",
|
|
1698
|
+
"skill",
|
|
1699
|
+
"agent"
|
|
1700
|
+
]);
|
|
1701
|
+
var triggerCollisionAnalyzer = {
|
|
1702
|
+
id: ID16,
|
|
1708
1703
|
pluginId: "core",
|
|
1709
1704
|
kind: "analyzer",
|
|
1710
|
-
version: "1.0.0",
|
|
1711
|
-
description: "Detects and flags leftover job result files (no live job references them). Cleanup via `sm job prune --orphan-files`.",
|
|
1712
|
-
stability: "stable",
|
|
1713
1705
|
mode: "deterministic",
|
|
1706
|
+
version: "1.0.0",
|
|
1707
|
+
description: "Detects and flags two or more nodes claiming the same `/command` or `@agent` name.",
|
|
1708
|
+
// Two claim-collection passes (advertisement + invocation) feeding
|
|
1709
|
+
// the bucket map. Per-bucket analysis lives in `analyzeTriggerBucket`.
|
|
1710
|
+
// eslint-disable-next-line complexity
|
|
1714
1711
|
evaluate(ctx) {
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1712
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1713
|
+
const push = (key, claim) => {
|
|
1714
|
+
const bucket = buckets.get(key) ?? [];
|
|
1715
|
+
bucket.push(claim);
|
|
1716
|
+
buckets.set(key, bucket);
|
|
1717
|
+
};
|
|
1718
|
+
for (const node of ctx.nodes) {
|
|
1719
|
+
if (!ADVERTISING_KINDS.has(node.kind)) continue;
|
|
1720
|
+
const raw = node.frontmatter?.["name"];
|
|
1721
|
+
if (typeof raw !== "string" || raw.length === 0) continue;
|
|
1722
|
+
const normalized = `/${normalizeTrigger(raw)}`;
|
|
1723
|
+
if (normalized === "/") continue;
|
|
1724
|
+
push(normalized, {
|
|
1725
|
+
kind: "advertiser",
|
|
1726
|
+
token: node.path,
|
|
1727
|
+
nodeId: node.path,
|
|
1728
|
+
canonicalForm: `/${raw}`
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
for (const link2 of ctx.links) {
|
|
1732
|
+
const normalized = link2.trigger?.normalizedTrigger;
|
|
1733
|
+
if (!normalized) continue;
|
|
1734
|
+
push(normalized, {
|
|
1735
|
+
kind: "invocation",
|
|
1736
|
+
token: link2.target,
|
|
1737
|
+
nodeId: link2.source
|
|
1725
1738
|
});
|
|
1726
1739
|
}
|
|
1740
|
+
const issues = [];
|
|
1741
|
+
for (const [normalized, claims] of buckets) {
|
|
1742
|
+
const issue = analyzeTriggerBucket(normalized, claims);
|
|
1743
|
+
if (issue) issues.push(issue);
|
|
1744
|
+
}
|
|
1727
1745
|
return issues;
|
|
1728
1746
|
}
|
|
1729
1747
|
};
|
|
1748
|
+
function analyzeTriggerBucket(normalized, claims) {
|
|
1749
|
+
const advertiserPaths = [
|
|
1750
|
+
...new Set(claims.filter((c) => c.kind === "advertiser").map((c) => c.token))
|
|
1751
|
+
].sort();
|
|
1752
|
+
const invocationTargets = [
|
|
1753
|
+
...new Set(claims.filter((c) => c.kind === "invocation").map((c) => c.token))
|
|
1754
|
+
].sort();
|
|
1755
|
+
const advertisers = claims.filter(
|
|
1756
|
+
(c) => c.kind === "advertiser"
|
|
1757
|
+
);
|
|
1758
|
+
const advertiserAmbiguous = advertiserPaths.length >= 2;
|
|
1759
|
+
const invocationAmbiguous = invocationTargets.length >= 2;
|
|
1760
|
+
const canonicalForms = new Set(advertisers.map((a) => a.canonicalForm));
|
|
1761
|
+
const nonCanonicalInvocations = invocationTargets.filter((t) => !canonicalForms.has(t));
|
|
1762
|
+
const crossKindAmbiguous = advertiserPaths.length === 1 && nonCanonicalInvocations.length >= 1;
|
|
1763
|
+
if (!advertiserAmbiguous && !invocationAmbiguous && !crossKindAmbiguous) {
|
|
1764
|
+
return null;
|
|
1765
|
+
}
|
|
1766
|
+
const nodeIds = [...new Set(claims.map((c) => c.nodeId))].sort();
|
|
1767
|
+
const parts = [];
|
|
1768
|
+
if (advertiserAmbiguous) {
|
|
1769
|
+
parts.push(
|
|
1770
|
+
tx(TRIGGER_COLLISION_TEXTS.partAdvertisers, {
|
|
1771
|
+
count: advertiserPaths.length,
|
|
1772
|
+
paths: advertiserPaths.join(", ")
|
|
1773
|
+
})
|
|
1774
|
+
);
|
|
1775
|
+
}
|
|
1776
|
+
if (invocationAmbiguous) {
|
|
1777
|
+
parts.push(
|
|
1778
|
+
tx(TRIGGER_COLLISION_TEXTS.partInvocations, {
|
|
1779
|
+
count: invocationTargets.length,
|
|
1780
|
+
forms: invocationTargets.join(", ")
|
|
1781
|
+
})
|
|
1782
|
+
);
|
|
1783
|
+
} else if (crossKindAmbiguous) {
|
|
1784
|
+
const template = nonCanonicalInvocations.length > 1 ? TRIGGER_COLLISION_TEXTS.partNonCanonicalPlural : TRIGGER_COLLISION_TEXTS.partNonCanonicalSingular;
|
|
1785
|
+
parts.push(
|
|
1786
|
+
tx(template, {
|
|
1787
|
+
forms: nonCanonicalInvocations.join(", "),
|
|
1788
|
+
advertiser: advertiserPaths[0]
|
|
1789
|
+
})
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
const message = parts.length === 2 ? tx(TRIGGER_COLLISION_TEXTS.messageTwoParts, {
|
|
1793
|
+
normalized,
|
|
1794
|
+
first: parts[0],
|
|
1795
|
+
second: parts[1]
|
|
1796
|
+
}) : tx(TRIGGER_COLLISION_TEXTS.messageOnePart, {
|
|
1797
|
+
normalized,
|
|
1798
|
+
part: parts[0]
|
|
1799
|
+
});
|
|
1800
|
+
return {
|
|
1801
|
+
analyzerId: ID16,
|
|
1802
|
+
severity: "error",
|
|
1803
|
+
nodeIds,
|
|
1804
|
+
message,
|
|
1805
|
+
data: {
|
|
1806
|
+
normalizedTrigger: normalized,
|
|
1807
|
+
invocationTargets,
|
|
1808
|
+
advertiserPaths
|
|
1809
|
+
}
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1730
1812
|
|
|
1731
|
-
//
|
|
1813
|
+
// plugins/core/analyzers/unknown-field/index.ts
|
|
1732
1814
|
import { readFileSync } from "fs";
|
|
1733
1815
|
import { dirname, resolve as resolve2 } from "path";
|
|
1734
1816
|
import { createRequire } from "module";
|
|
@@ -1741,7 +1823,7 @@ function applyAjvFormats(ajv) {
|
|
|
1741
1823
|
addFormats(ajv);
|
|
1742
1824
|
}
|
|
1743
1825
|
|
|
1744
|
-
//
|
|
1826
|
+
// plugins/core/analyzers/unknown-field/text.ts
|
|
1745
1827
|
var UNKNOWN_FIELD_TEXTS = {
|
|
1746
1828
|
/** Key inside `annotations:` is not in the curated catalog. */
|
|
1747
1829
|
unknownAnnotationKey: "{{path}}: sidecar annotations contain unknown key '{{key}}' (not in annotations.schema.json catalog).",
|
|
@@ -1755,18 +1837,17 @@ var UNKNOWN_FIELD_TEXTS = {
|
|
|
1755
1837
|
alertTooltipMany: "This node has {{count}} unknown fields in its sidecar. Open the inspector for details."
|
|
1756
1838
|
};
|
|
1757
1839
|
|
|
1758
|
-
//
|
|
1759
|
-
var
|
|
1840
|
+
// plugins/core/analyzers/unknown-field/index.ts
|
|
1841
|
+
var ID17 = "unknown-field";
|
|
1760
1842
|
var RESERVED_ROOT_BLOCKS = /* @__PURE__ */ new Set(["identity", "annotations", "settings", "audit"]);
|
|
1761
1843
|
var unknownFieldAnalyzer = {
|
|
1762
|
-
id:
|
|
1844
|
+
id: ID17,
|
|
1763
1845
|
pluginId: "core",
|
|
1764
1846
|
kind: "analyzer",
|
|
1765
1847
|
version: "1.0.0",
|
|
1766
1848
|
description: "Detects and flags typos or unrecognized keys in sidecars (`.sm`).",
|
|
1767
|
-
stability: "stable",
|
|
1768
1849
|
mode: "deterministic",
|
|
1769
|
-
|
|
1850
|
+
ui: {
|
|
1770
1851
|
// Corner badge on the graph card; count omitted when there is a
|
|
1771
1852
|
// single unknown field (avoids a noisy "icon + 1" chip).
|
|
1772
1853
|
alert: {
|
|
@@ -1818,7 +1899,7 @@ var unknownFieldAnalyzer = {
|
|
|
1818
1899
|
for (const key of Object.keys(annotations)) {
|
|
1819
1900
|
if (!knownAnnotationKeys.has(key)) {
|
|
1820
1901
|
issues.push({
|
|
1821
|
-
analyzerId:
|
|
1902
|
+
analyzerId: ID17,
|
|
1822
1903
|
severity: "warn",
|
|
1823
1904
|
nodeIds: [node.path],
|
|
1824
1905
|
message: tx(UNKNOWN_FIELD_TEXTS.unknownAnnotationKey, {
|
|
@@ -1845,7 +1926,7 @@ var unknownFieldAnalyzer = {
|
|
|
1845
1926
|
if (validator(value)) continue;
|
|
1846
1927
|
const errors = (validator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
|
|
1847
1928
|
issues.push({
|
|
1848
|
-
analyzerId:
|
|
1929
|
+
analyzerId: ID17,
|
|
1849
1930
|
severity: "warn",
|
|
1850
1931
|
nodeIds: [node.path],
|
|
1851
1932
|
message: tx(UNKNOWN_FIELD_TEXTS.pluginNamespaceInvalid, {
|
|
@@ -1861,7 +1942,7 @@ var unknownFieldAnalyzer = {
|
|
|
1861
1942
|
continue;
|
|
1862
1943
|
}
|
|
1863
1944
|
issues.push({
|
|
1864
|
-
analyzerId:
|
|
1945
|
+
analyzerId: ID17,
|
|
1865
1946
|
severity: "warn",
|
|
1866
1947
|
nodeIds: [node.path],
|
|
1867
1948
|
message: tx(UNKNOWN_FIELD_TEXTS.unknownRootKey, {
|
|
@@ -1922,181 +2003,15 @@ function indexNamespacedContributions(contributions) {
|
|
|
1922
2003
|
function indexRootContributions(contributions) {
|
|
1923
2004
|
const out = /* @__PURE__ */ new Set();
|
|
1924
2005
|
for (const entry of contributions) {
|
|
1925
|
-
if (entry.location === "root") out.add(entry.key);
|
|
1926
|
-
}
|
|
1927
|
-
return out;
|
|
1928
|
-
}
|
|
1929
|
-
function collectPluginIds(contributions) {
|
|
1930
|
-
const out = /* @__PURE__ */ new Set();
|
|
1931
|
-
for (const entry of contributions) out.add(entry.pluginId);
|
|
1932
|
-
return out;
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
// built-in-plugins/analyzers/contribution-orphan/index.ts
|
|
1936
|
-
var ID16 = "contribution-orphan";
|
|
1937
|
-
var contributionOrphanAnalyzer = {
|
|
1938
|
-
id: ID16,
|
|
1939
|
-
pluginId: "core",
|
|
1940
|
-
kind: "analyzer",
|
|
1941
|
-
version: "0.0.0",
|
|
1942
|
-
description: "Detects and warns about plugin data referencing nodes renamed or deleted in the latest scan.",
|
|
1943
|
-
stability: "experimental",
|
|
1944
|
-
mode: "deterministic",
|
|
1945
|
-
evaluate(_ctx) {
|
|
1946
|
-
return [];
|
|
1947
|
-
}
|
|
1948
|
-
};
|
|
1949
|
-
|
|
1950
|
-
// kernel/util/safe-text.ts
|
|
1951
|
-
var ANSI_ESCAPE_RE = /[][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
1952
|
-
var C0_CONTROL_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
|
|
1953
|
-
function sanitizeForTerminal(text) {
|
|
1954
|
-
return text.replace(ANSI_ESCAPE_RE, "").replace(C0_CONTROL_RE, "");
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
// built-in-plugins/i18n/ascii.texts.ts
|
|
1958
|
-
var ASCII_FORMATTER_TEXTS = {
|
|
1959
|
-
/** Header line: `skill-map graph: N nodes, M links, K issues`. */
|
|
1960
|
-
header: "skill-map graph: {{nodes}} nodes, {{links}} links, {{issues}} issues",
|
|
1961
|
-
/** Per-node-kind section header: `## <kind> (<count>)`. */
|
|
1962
|
-
kindSectionHeader: "## {{kind}} ({{count}})",
|
|
1963
|
-
/** Plain node bullet: `- <path>`. */
|
|
1964
|
-
nodeBullet: "- {{path}}",
|
|
1965
|
-
/** Node bullet with title suffix: `- <path>: "<title>"`. */
|
|
1966
|
-
nodeBulletWithTitle: '- {{path}}: "{{title}}"',
|
|
1967
|
-
/** `## links (<count>)` section header. */
|
|
1968
|
-
linksSectionHeader: "## links ({{count}})",
|
|
1969
|
-
/** Link bullet: `- <source> --<kind>--> <target> [<confidence>]`. */
|
|
1970
|
-
linkBullet: "- {{source}} --{{kind}}--> {{target}} [{{confidence}}]",
|
|
1971
|
-
/** `## issues (<count>)` section header. */
|
|
1972
|
-
issuesSectionHeader: "## issues ({{count}})",
|
|
1973
|
-
/** Issue bullet: `- [<severity>] <analyzerId>: <message>`. */
|
|
1974
|
-
issueBullet: "- [{{severity}}] {{analyzerId}}: {{message}}"
|
|
1975
|
-
};
|
|
1976
|
-
|
|
1977
|
-
// built-in-plugins/formatters/ascii/index.ts
|
|
1978
|
-
var ID17 = "ascii";
|
|
1979
|
-
var KIND_ORDER = ["agent", "command", "skill", "markdown"];
|
|
1980
|
-
var asciiFormatter = {
|
|
1981
|
-
id: ID17,
|
|
1982
|
-
pluginId: "core",
|
|
1983
|
-
kind: "formatter",
|
|
1984
|
-
version: "1.0.0",
|
|
1985
|
-
description: "Renders the scan as plain text, grouped by kind, arrows, and issues. Used by `sm scan --format=ascii`.",
|
|
1986
|
-
stability: "stable",
|
|
1987
|
-
formatId: "ascii",
|
|
1988
|
-
// ASCII tree formatter, header + per-kind sections + per-issue
|
|
1989
|
-
// section. Each section iterates and renders; splitting per section
|
|
1990
|
-
// would multiply the for-loop boilerplate.
|
|
1991
|
-
// eslint-disable-next-line complexity
|
|
1992
|
-
format(ctx) {
|
|
1993
|
-
const out = [];
|
|
1994
|
-
out.push(
|
|
1995
|
-
tx(ASCII_FORMATTER_TEXTS.header, {
|
|
1996
|
-
nodes: ctx.nodes.length,
|
|
1997
|
-
links: ctx.links.length,
|
|
1998
|
-
issues: ctx.issues.length
|
|
1999
|
-
}),
|
|
2000
|
-
""
|
|
2001
|
-
);
|
|
2002
|
-
const byKind = /* @__PURE__ */ new Map();
|
|
2003
|
-
for (const node of ctx.nodes) {
|
|
2004
|
-
if (!byKind.has(node.kind)) byKind.set(node.kind, []);
|
|
2005
|
-
byKind.get(node.kind).push(node);
|
|
2006
|
-
}
|
|
2007
|
-
const renderedKinds = /* @__PURE__ */ new Set();
|
|
2008
|
-
for (const kind of KIND_ORDER) {
|
|
2009
|
-
const group = byKind.get(kind);
|
|
2010
|
-
if (!group || group.length === 0) continue;
|
|
2011
|
-
renderSection(out, kind, group);
|
|
2012
|
-
renderedKinds.add(kind);
|
|
2013
|
-
}
|
|
2014
|
-
const extraKinds = [...byKind.keys()].filter((k) => !renderedKinds.has(k)).sort();
|
|
2015
|
-
for (const kind of extraKinds) {
|
|
2016
|
-
const group = byKind.get(kind);
|
|
2017
|
-
if (!group || group.length === 0) continue;
|
|
2018
|
-
renderSection(out, kind, group);
|
|
2019
|
-
}
|
|
2020
|
-
if (ctx.links.length > 0) {
|
|
2021
|
-
out.push(tx(ASCII_FORMATTER_TEXTS.linksSectionHeader, { count: ctx.links.length }));
|
|
2022
|
-
const sorted = [...ctx.links].sort((a, b) => {
|
|
2023
|
-
const aKey = `${a.source}\0${a.kind}\0${a.target}`;
|
|
2024
|
-
const bKey = `${b.source}\0${b.kind}\0${b.target}`;
|
|
2025
|
-
return aKey.localeCompare(bKey);
|
|
2026
|
-
});
|
|
2027
|
-
for (const link2 of sorted) {
|
|
2028
|
-
out.push(
|
|
2029
|
-
tx(ASCII_FORMATTER_TEXTS.linkBullet, {
|
|
2030
|
-
source: sanitizeForTerminal(link2.source),
|
|
2031
|
-
kind: sanitizeForTerminal(link2.kind),
|
|
2032
|
-
target: sanitizeForTerminal(link2.target),
|
|
2033
|
-
confidence: link2.confidence
|
|
2034
|
-
})
|
|
2035
|
-
);
|
|
2036
|
-
}
|
|
2037
|
-
out.push("");
|
|
2038
|
-
}
|
|
2039
|
-
if (ctx.issues.length > 0) {
|
|
2040
|
-
out.push(tx(ASCII_FORMATTER_TEXTS.issuesSectionHeader, { count: ctx.issues.length }));
|
|
2041
|
-
for (const issue of ctx.issues) {
|
|
2042
|
-
out.push(
|
|
2043
|
-
tx(ASCII_FORMATTER_TEXTS.issueBullet, {
|
|
2044
|
-
severity: issue.severity,
|
|
2045
|
-
analyzerId: sanitizeForTerminal(issue.analyzerId),
|
|
2046
|
-
message: sanitizeForTerminal(issue.message)
|
|
2047
|
-
})
|
|
2048
|
-
);
|
|
2049
|
-
}
|
|
2050
|
-
out.push("");
|
|
2051
|
-
}
|
|
2052
|
-
return out.join("\n");
|
|
2053
|
-
}
|
|
2054
|
-
};
|
|
2055
|
-
function pickTitle(node) {
|
|
2056
|
-
const name = node.frontmatter?.["name"];
|
|
2057
|
-
return typeof name === "string" && name.length > 0 ? name : null;
|
|
2058
|
-
}
|
|
2059
|
-
function renderSection(out, kind, group) {
|
|
2060
|
-
const sorted = [...group].sort((a, b) => a.path.localeCompare(b.path));
|
|
2061
|
-
out.push(
|
|
2062
|
-
tx(ASCII_FORMATTER_TEXTS.kindSectionHeader, {
|
|
2063
|
-
kind: sanitizeForTerminal(kind),
|
|
2064
|
-
count: sorted.length
|
|
2065
|
-
})
|
|
2066
|
-
);
|
|
2067
|
-
for (const node of sorted) {
|
|
2068
|
-
const title = pickTitle(node);
|
|
2069
|
-
out.push(
|
|
2070
|
-
title ? tx(ASCII_FORMATTER_TEXTS.nodeBulletWithTitle, {
|
|
2071
|
-
path: sanitizeForTerminal(node.path),
|
|
2072
|
-
title: sanitizeForTerminal(title)
|
|
2073
|
-
}) : tx(ASCII_FORMATTER_TEXTS.nodeBullet, { path: sanitizeForTerminal(node.path) })
|
|
2074
|
-
);
|
|
2075
|
-
}
|
|
2076
|
-
out.push("");
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
// built-in-plugins/formatters/json/index.ts
|
|
2080
|
-
var ID18 = "json";
|
|
2081
|
-
var jsonFormatter = {
|
|
2082
|
-
id: ID18,
|
|
2083
|
-
pluginId: "core",
|
|
2084
|
-
kind: "formatter",
|
|
2085
|
-
version: "1.0.0",
|
|
2086
|
-
description: "Renders the persisted scan as JSON (conforms to `scan-result.schema.json` when the full ScanResult is available). Used by `sm graph --format json` and `GET /api/graph?format=json`.",
|
|
2087
|
-
stability: "stable",
|
|
2088
|
-
formatId: ID18,
|
|
2089
|
-
format(ctx) {
|
|
2090
|
-
if (ctx.scanResult !== void 0) {
|
|
2091
|
-
return JSON.stringify(ctx.scanResult);
|
|
2092
|
-
}
|
|
2093
|
-
return JSON.stringify({
|
|
2094
|
-
nodes: ctx.nodes,
|
|
2095
|
-
links: ctx.links,
|
|
2096
|
-
issues: ctx.issues
|
|
2097
|
-
});
|
|
2006
|
+
if (entry.location === "root") out.add(entry.key);
|
|
2098
2007
|
}
|
|
2099
|
-
|
|
2008
|
+
return out;
|
|
2009
|
+
}
|
|
2010
|
+
function collectPluginIds(contributions) {
|
|
2011
|
+
const out = /* @__PURE__ */ new Set();
|
|
2012
|
+
for (const entry of contributions) out.add(entry.pluginId);
|
|
2013
|
+
return out;
|
|
2014
|
+
}
|
|
2100
2015
|
|
|
2101
2016
|
// kernel/adapters/schema-validators.ts
|
|
2102
2017
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -2138,6 +2053,7 @@ var SCHEMA_FILES = {
|
|
|
2138
2053
|
"conformance-case": "schemas/conformance-case.schema.json",
|
|
2139
2054
|
"history-stats": "schemas/history-stats.schema.json",
|
|
2140
2055
|
"extension-provider": "schemas/extensions/provider.schema.json",
|
|
2056
|
+
"extension-provider-kind": "schemas/extensions/provider-kind.schema.json",
|
|
2141
2057
|
"extension-extractor": "schemas/extensions/extractor.schema.json",
|
|
2142
2058
|
"extension-analyzer": "schemas/extensions/analyzer.schema.json",
|
|
2143
2059
|
"extension-action": "schemas/extensions/action.schema.json",
|
|
@@ -2304,7 +2220,7 @@ function existsSyncSafe(path) {
|
|
|
2304
2220
|
}
|
|
2305
2221
|
}
|
|
2306
2222
|
|
|
2307
|
-
//
|
|
2223
|
+
// plugins/core/analyzers/validate-all/text.ts
|
|
2308
2224
|
var VALIDATE_ALL_TEXTS = {
|
|
2309
2225
|
/** `Node <path> failed schema validation: <errors>` */
|
|
2310
2226
|
nodeFailure: "Node {{path}} failed schema validation: {{errors}}",
|
|
@@ -2318,17 +2234,16 @@ var VALIDATE_ALL_TEXTS = {
|
|
|
2318
2234
|
alertTooltipMany: "{{count}} schema validation issues on this node."
|
|
2319
2235
|
};
|
|
2320
2236
|
|
|
2321
|
-
//
|
|
2322
|
-
var
|
|
2237
|
+
// plugins/core/analyzers/validate-all/index.ts
|
|
2238
|
+
var ID18 = "validate-all";
|
|
2323
2239
|
var validateAllAnalyzer = {
|
|
2324
|
-
id:
|
|
2240
|
+
id: ID18,
|
|
2325
2241
|
pluginId: "core",
|
|
2326
2242
|
kind: "analyzer",
|
|
2327
2243
|
version: "1.0.0",
|
|
2328
2244
|
description: "Detects and flags nodes or links violating the project schemas.",
|
|
2329
|
-
stability: "stable",
|
|
2330
2245
|
mode: "deterministic",
|
|
2331
|
-
|
|
2246
|
+
ui: {
|
|
2332
2247
|
// Corner badge on the graph card; surfaces when the node body /
|
|
2333
2248
|
// frontmatter fails schema validation (parse error, missing
|
|
2334
2249
|
// `name`/`description`, malformed YAML, etc.). Same visual
|
|
@@ -2385,7 +2300,7 @@ function collectNodeFindings(v, node, out) {
|
|
|
2385
2300
|
const result = v.validate("node", toNodeForSchema(node));
|
|
2386
2301
|
if (result.ok) return;
|
|
2387
2302
|
out.push({
|
|
2388
|
-
analyzerId:
|
|
2303
|
+
analyzerId: ID18,
|
|
2389
2304
|
severity: "error",
|
|
2390
2305
|
nodeIds: [node.path],
|
|
2391
2306
|
message: tx(VALIDATE_ALL_TEXTS.nodeFailure, {
|
|
@@ -2404,7 +2319,7 @@ function collectFrontmatterBaseFindings(node, out) {
|
|
|
2404
2319
|
if (isMissingStringField(fm, "description")) missing.push("description");
|
|
2405
2320
|
if (missing.length === 0) return;
|
|
2406
2321
|
out.push({
|
|
2407
|
-
analyzerId:
|
|
2322
|
+
analyzerId: ID18,
|
|
2408
2323
|
// `warn` (not `error`) so the default `sm scan` exit code stays
|
|
2409
2324
|
// 0 even when nodes are missing frontmatter base fields. Strict
|
|
2410
2325
|
// mode (`sm scan --strict`) still escalates to exit 1. Matches
|
|
@@ -2426,7 +2341,7 @@ function collectLinkFindings(v, link2, out) {
|
|
|
2426
2341
|
const result = v.validate("link", toLinkForSchema(link2));
|
|
2427
2342
|
if (result.ok) return;
|
|
2428
2343
|
out.push({
|
|
2429
|
-
analyzerId:
|
|
2344
|
+
analyzerId: ID18,
|
|
2430
2345
|
severity: "error",
|
|
2431
2346
|
nodeIds: [link2.source],
|
|
2432
2347
|
message: tx(VALIDATE_ALL_TEXTS.linkFailure, {
|
|
@@ -2466,120 +2381,154 @@ function toLinkForSchema(link2) {
|
|
|
2466
2381
|
};
|
|
2467
2382
|
}
|
|
2468
2383
|
|
|
2469
|
-
// kernel/util/
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
return out;
|
|
2475
|
-
}
|
|
2476
|
-
function indexByCanonicalName(nodes, out) {
|
|
2477
|
-
for (const node of nodes) {
|
|
2478
|
-
const raw = canonicalName(node);
|
|
2479
|
-
if (raw === null) continue;
|
|
2480
|
-
const key = normalizeTrigger(raw);
|
|
2481
|
-
if (!out.has(key)) out.set(key, node.path);
|
|
2482
|
-
}
|
|
2384
|
+
// kernel/util/safe-text.ts
|
|
2385
|
+
var ANSI_ESCAPE_RE = /[][[\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\d/#&.:=?%@~_]+)*|[a-zA-Z\d]+(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
2386
|
+
var C0_CONTROL_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
|
|
2387
|
+
function sanitizeForTerminal(text) {
|
|
2388
|
+
return text.replace(ANSI_ESCAPE_RE, "").replace(C0_CONTROL_RE, "");
|
|
2483
2389
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2390
|
+
|
|
2391
|
+
// plugins/core/formatters/ascii/text.ts
|
|
2392
|
+
var ASCII_FORMATTER_TEXTS = {
|
|
2393
|
+
/** Header line: `skill-map graph: N nodes, M links, K issues`. */
|
|
2394
|
+
header: "skill-map graph: {{nodes}} nodes, {{links}} links, {{issues}} issues",
|
|
2395
|
+
/** Per-node-kind section header: `## <kind> (<count>)`. */
|
|
2396
|
+
kindSectionHeader: "## {{kind}} ({{count}})",
|
|
2397
|
+
/** Plain node bullet: `- <path>`. */
|
|
2398
|
+
nodeBullet: "- {{path}}",
|
|
2399
|
+
/** Node bullet with title suffix: `- <path>: "<title>"`. */
|
|
2400
|
+
nodeBulletWithTitle: '- {{path}}: "{{title}}"',
|
|
2401
|
+
/** `## links (<count>)` section header. */
|
|
2402
|
+
linksSectionHeader: "## links ({{count}})",
|
|
2403
|
+
/** Link bullet: `- <source> --<kind>--> <target> [<confidence>]`. */
|
|
2404
|
+
linkBullet: "- {{source}} --{{kind}}--> {{target}} [{{confidence}}]",
|
|
2405
|
+
/** `## issues (<count>)` section header. */
|
|
2406
|
+
issuesSectionHeader: "## issues ({{count}})",
|
|
2407
|
+
/** Issue bullet: `- [<severity>] <analyzerId>: <message>`. */
|
|
2408
|
+
issueBullet: "- [{{severity}}] {{analyzerId}}: {{message}}"
|
|
2409
|
+
};
|
|
2410
|
+
|
|
2411
|
+
// plugins/core/formatters/ascii/index.ts
|
|
2412
|
+
var ID19 = "ascii";
|
|
2413
|
+
var KIND_ORDER = ["agent", "command", "skill", "markdown"];
|
|
2414
|
+
var asciiFormatter = {
|
|
2415
|
+
id: ID19,
|
|
2416
|
+
pluginId: "core",
|
|
2417
|
+
kind: "formatter",
|
|
2418
|
+
formatId: ID19,
|
|
2419
|
+
version: "1.0.0",
|
|
2420
|
+
description: "Renders the scan as plain text, grouped by kind, arrows, and issues. Used by `sm scan --format=ascii`.",
|
|
2421
|
+
// ASCII tree formatter, header + per-kind sections + per-issue
|
|
2422
|
+
// section. Each section iterates and renders; splitting per section
|
|
2423
|
+
// would multiply the for-loop boilerplate.
|
|
2424
|
+
// eslint-disable-next-line complexity
|
|
2425
|
+
format(ctx) {
|
|
2426
|
+
const out = [];
|
|
2427
|
+
out.push(
|
|
2428
|
+
tx(ASCII_FORMATTER_TEXTS.header, {
|
|
2429
|
+
nodes: ctx.nodes.length,
|
|
2430
|
+
links: ctx.links.length,
|
|
2431
|
+
issues: ctx.issues.length
|
|
2432
|
+
}),
|
|
2433
|
+
""
|
|
2434
|
+
);
|
|
2435
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
2436
|
+
for (const node of ctx.nodes) {
|
|
2437
|
+
if (!byKind.has(node.kind)) byKind.set(node.kind, []);
|
|
2438
|
+
byKind.get(node.kind).push(node);
|
|
2439
|
+
}
|
|
2440
|
+
const renderedKinds = /* @__PURE__ */ new Set();
|
|
2441
|
+
for (const kind of KIND_ORDER) {
|
|
2442
|
+
const group = byKind.get(kind);
|
|
2443
|
+
if (!group || group.length === 0) continue;
|
|
2444
|
+
renderSection(out, kind, group);
|
|
2445
|
+
renderedKinds.add(kind);
|
|
2446
|
+
}
|
|
2447
|
+
const extraKinds = [...byKind.keys()].filter((k) => !renderedKinds.has(k)).sort();
|
|
2448
|
+
for (const kind of extraKinds) {
|
|
2449
|
+
const group = byKind.get(kind);
|
|
2450
|
+
if (!group || group.length === 0) continue;
|
|
2451
|
+
renderSection(out, kind, group);
|
|
2452
|
+
}
|
|
2453
|
+
if (ctx.links.length > 0) {
|
|
2454
|
+
out.push(tx(ASCII_FORMATTER_TEXTS.linksSectionHeader, { count: ctx.links.length }));
|
|
2455
|
+
const sorted = [...ctx.links].sort((a, b) => {
|
|
2456
|
+
const aKey = `${a.source}\0${a.kind}\0${a.target}`;
|
|
2457
|
+
const bKey = `${b.source}\0${b.kind}\0${b.target}`;
|
|
2458
|
+
return aKey.localeCompare(bKey);
|
|
2459
|
+
});
|
|
2460
|
+
for (const link2 of sorted) {
|
|
2461
|
+
out.push(
|
|
2462
|
+
tx(ASCII_FORMATTER_TEXTS.linkBullet, {
|
|
2463
|
+
source: sanitizeForTerminal(link2.source),
|
|
2464
|
+
kind: sanitizeForTerminal(link2.kind),
|
|
2465
|
+
target: sanitizeForTerminal(link2.target),
|
|
2466
|
+
confidence: link2.confidence
|
|
2467
|
+
})
|
|
2468
|
+
);
|
|
2469
|
+
}
|
|
2470
|
+
out.push("");
|
|
2471
|
+
}
|
|
2472
|
+
if (ctx.issues.length > 0) {
|
|
2473
|
+
out.push(tx(ASCII_FORMATTER_TEXTS.issuesSectionHeader, { count: ctx.issues.length }));
|
|
2474
|
+
for (const issue of ctx.issues) {
|
|
2475
|
+
out.push(
|
|
2476
|
+
tx(ASCII_FORMATTER_TEXTS.issueBullet, {
|
|
2477
|
+
severity: issue.severity,
|
|
2478
|
+
analyzerId: sanitizeForTerminal(issue.analyzerId),
|
|
2479
|
+
message: sanitizeForTerminal(issue.message)
|
|
2480
|
+
})
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
out.push("");
|
|
2484
|
+
}
|
|
2485
|
+
return out.join("\n");
|
|
2491
2486
|
}
|
|
2487
|
+
};
|
|
2488
|
+
function pickTitle(node) {
|
|
2489
|
+
const name = node.frontmatter?.["name"];
|
|
2490
|
+
return typeof name === "string" && name.length > 0 ? name : null;
|
|
2492
2491
|
}
|
|
2493
|
-
function
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2492
|
+
function renderSection(out, kind, group) {
|
|
2493
|
+
const sorted = [...group].sort((a, b) => a.path.localeCompare(b.path));
|
|
2494
|
+
out.push(
|
|
2495
|
+
tx(ASCII_FORMATTER_TEXTS.kindSectionHeader, {
|
|
2496
|
+
kind: sanitizeForTerminal(kind),
|
|
2497
|
+
count: sorted.length
|
|
2498
|
+
})
|
|
2499
|
+
);
|
|
2500
|
+
for (const node of sorted) {
|
|
2501
|
+
const title = pickTitle(node);
|
|
2502
|
+
out.push(
|
|
2503
|
+
title ? tx(ASCII_FORMATTER_TEXTS.nodeBulletWithTitle, {
|
|
2504
|
+
path: sanitizeForTerminal(node.path),
|
|
2505
|
+
title: sanitizeForTerminal(title)
|
|
2506
|
+
}) : tx(ASCII_FORMATTER_TEXTS.nodeBullet, { path: sanitizeForTerminal(node.path) })
|
|
2507
|
+
);
|
|
2504
2508
|
}
|
|
2505
|
-
|
|
2506
|
-
}
|
|
2507
|
-
function resolveLinkTargetToPath(link2, nameIndex) {
|
|
2508
|
-
const raw = link2.target;
|
|
2509
|
-
const sigil = raw.charAt(0);
|
|
2510
|
-
if (sigil !== "/" && sigil !== "@") return raw;
|
|
2511
|
-
const normalizedTrigger = link2.trigger?.normalizedTrigger;
|
|
2512
|
-
const normalized = typeof normalizedTrigger === "string" ? normalizedTrigger.replace(/^[/@]/, "").trim() : normalizeTrigger(raw.slice(1));
|
|
2513
|
-
const resolved = nameIndex.get(normalized);
|
|
2514
|
-
return resolved ?? raw;
|
|
2509
|
+
out.push("");
|
|
2515
2510
|
}
|
|
2516
2511
|
|
|
2517
|
-
//
|
|
2518
|
-
var ID20 = "
|
|
2519
|
-
var
|
|
2512
|
+
// plugins/core/formatters/json/index.ts
|
|
2513
|
+
var ID20 = "json";
|
|
2514
|
+
var jsonFormatter = {
|
|
2520
2515
|
id: ID20,
|
|
2521
2516
|
pluginId: "core",
|
|
2522
|
-
kind: "
|
|
2517
|
+
kind: "formatter",
|
|
2523
2518
|
version: "1.0.0",
|
|
2524
|
-
description: "
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
slot: "card.footer.left",
|
|
2530
|
-
icon: "pi-download",
|
|
2531
|
-
label: "incoming links",
|
|
2532
|
-
emitWhenEmpty: false,
|
|
2533
|
-
priority: 10
|
|
2534
|
-
},
|
|
2535
|
-
linksOut: {
|
|
2536
|
-
slot: "card.footer.left",
|
|
2537
|
-
icon: "pi-upload",
|
|
2538
|
-
label: "outgoing links",
|
|
2539
|
-
emitWhenEmpty: false,
|
|
2540
|
-
priority: 20
|
|
2541
|
-
}
|
|
2542
|
-
},
|
|
2543
|
-
evaluate(ctx) {
|
|
2544
|
-
const nameIndex = buildNameIndex(ctx.nodes);
|
|
2545
|
-
const perTarget = /* @__PURE__ */ new Map();
|
|
2546
|
-
const perSource = /* @__PURE__ */ new Map();
|
|
2547
|
-
for (const link2 of ctx.links) {
|
|
2548
|
-
const resolvedTarget = resolveLinkTargetToPath(link2, nameIndex);
|
|
2549
|
-
bump(perTarget, resolvedTarget, link2.kind);
|
|
2550
|
-
bump(perSource, link2.source, link2.kind);
|
|
2551
|
-
}
|
|
2552
|
-
for (const node of ctx.nodes) {
|
|
2553
|
-
emitChip(ctx, node.path, "linksIn", perTarget.get(node.path));
|
|
2554
|
-
emitChip(ctx, node.path, "linksOut", perSource.get(node.path));
|
|
2519
|
+
description: "Renders the persisted scan as JSON (conforms to `scan-result.schema.json` when the full ScanResult is available). Used by `sm graph --format json` and `GET /api/graph?format=json`.",
|
|
2520
|
+
formatId: ID20,
|
|
2521
|
+
format(ctx) {
|
|
2522
|
+
if (ctx.scanResult !== void 0) {
|
|
2523
|
+
return JSON.stringify(ctx.scanResult);
|
|
2555
2524
|
}
|
|
2556
|
-
return
|
|
2525
|
+
return JSON.stringify({
|
|
2526
|
+
nodes: ctx.nodes,
|
|
2527
|
+
links: ctx.links,
|
|
2528
|
+
issues: ctx.issues
|
|
2529
|
+
});
|
|
2557
2530
|
}
|
|
2558
2531
|
};
|
|
2559
|
-
function bump(map, key, kind) {
|
|
2560
|
-
let byKind = map.get(key);
|
|
2561
|
-
if (!byKind) {
|
|
2562
|
-
byKind = /* @__PURE__ */ new Map();
|
|
2563
|
-
map.set(key, byKind);
|
|
2564
|
-
}
|
|
2565
|
-
byKind.set(kind, (byKind.get(kind) ?? 0) + 1);
|
|
2566
|
-
}
|
|
2567
|
-
function emitChip(ctx, nodePath, contributionId, byKind) {
|
|
2568
|
-
if (!byKind) return;
|
|
2569
|
-
let total = 0;
|
|
2570
|
-
for (const n of byKind.values()) total += n;
|
|
2571
|
-
if (total === 0) return;
|
|
2572
|
-
const capped = Math.min(total, 99);
|
|
2573
|
-
const direction = contributionId === "linksIn" ? "in" : "out";
|
|
2574
|
-
ctx.emitContribution(nodePath, contributionId, {
|
|
2575
|
-
value: capped,
|
|
2576
|
-
tooltip: formatBreakdown(byKind, direction)
|
|
2577
|
-
});
|
|
2578
|
-
}
|
|
2579
|
-
function formatBreakdown(byKind, direction) {
|
|
2580
|
-
const lines = [...byKind.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([kind, n]) => `${kind}: ${n}`);
|
|
2581
|
-
return [direction, ...lines].join("\n");
|
|
2582
|
-
}
|
|
2583
2532
|
|
|
2584
2533
|
// kernel/sidecar/parse.ts
|
|
2585
2534
|
import { existsSync, readFileSync as readFileSync3 } from "fs";
|
|
@@ -2706,7 +2655,7 @@ function resolveSpecRoot2() {
|
|
|
2706
2655
|
}
|
|
2707
2656
|
}
|
|
2708
2657
|
|
|
2709
|
-
//
|
|
2658
|
+
// plugins/core/actions/bump/index.ts
|
|
2710
2659
|
var ID21 = "bump";
|
|
2711
2660
|
var PLUGIN_ID = "core";
|
|
2712
2661
|
var bumpAction = {
|
|
@@ -2715,9 +2664,7 @@ var bumpAction = {
|
|
|
2715
2664
|
kind: "action",
|
|
2716
2665
|
version: "1.0.0",
|
|
2717
2666
|
description: "Marks a node as updated: bumps version, refreshes sidecar hashes, records the timestamp.",
|
|
2718
|
-
stability: "stable",
|
|
2719
2667
|
mode: "deterministic",
|
|
2720
|
-
reportSchemaRef: "https://skill-map.dev/spec/v0/bump-report.schema.json",
|
|
2721
2668
|
// The runtime contract uses generic <TInput, TReport>; bump narrows
|
|
2722
2669
|
// both. The cast is the standard pattern for built-ins that want
|
|
2723
2670
|
// typed local I/O while staying compatible with the open generic.
|
|
@@ -2770,7 +2717,7 @@ function pickCurrentVersion(overlay) {
|
|
|
2770
2717
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
2771
2718
|
}
|
|
2772
2719
|
|
|
2773
|
-
//
|
|
2720
|
+
// plugins/core/actions/mark-superseded/index.ts
|
|
2774
2721
|
var ID22 = "mark-superseded";
|
|
2775
2722
|
var PLUGIN_ID2 = "core";
|
|
2776
2723
|
var markSupersededAction = {
|
|
@@ -2779,9 +2726,7 @@ var markSupersededAction = {
|
|
|
2779
2726
|
kind: "action",
|
|
2780
2727
|
version: "0.0.0",
|
|
2781
2728
|
description: "Declares the current node as superseded by another (writes `supersededBy` to the sidecar). Paired with the `core/superseded` analyzer.",
|
|
2782
|
-
stability: "experimental",
|
|
2783
2729
|
mode: "deterministic",
|
|
2784
|
-
reportSchemaRef: "https://skill-map.dev/spec/v0/report-base-deterministic.schema.json",
|
|
2785
2730
|
invoke(_input, _ctx) {
|
|
2786
2731
|
const report = { ok: true, noop: true };
|
|
2787
2732
|
return { report };
|
|
@@ -2886,7 +2831,7 @@ var UPDATE_CHECK_TEXTS = {
|
|
|
2886
2831
|
// package.json
|
|
2887
2832
|
var package_default = {
|
|
2888
2833
|
name: "@skill-map/cli",
|
|
2889
|
-
version: "0.
|
|
2834
|
+
version: "0.29.0",
|
|
2890
2835
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
2891
2836
|
license: "MIT",
|
|
2892
2837
|
type: "module",
|
|
@@ -2939,16 +2884,19 @@ var package_default = {
|
|
|
2939
2884
|
"lint:fix": "eslint . --fix",
|
|
2940
2885
|
reference: "node scripts/build-reference.js",
|
|
2941
2886
|
"reference:check": "node scripts/build-reference.js --check",
|
|
2887
|
+
"build-built-ins": "node ../scripts/generate-built-ins.js",
|
|
2888
|
+
"built-ins:check": "node ../scripts/generate-built-ins.js --check",
|
|
2889
|
+
prebuild: "pnpm build-built-ins",
|
|
2942
2890
|
validate: "pnpm validate:compile && pnpm validate:test",
|
|
2943
|
-
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm reference:check",
|
|
2891
|
+
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm reference:check && pnpm built-ins:check",
|
|
2944
2892
|
"validate:test": "pnpm test:ci",
|
|
2945
2893
|
pretest: "tsup",
|
|
2946
2894
|
"pretest:coverage": "tsup",
|
|
2947
2895
|
"pretest:coverage:html": "tsup",
|
|
2948
|
-
test: "tsc --noEmit && node --import tsx --test --test-reporter=spec '
|
|
2949
|
-
"test:ci": "node --import tsx --test '
|
|
2950
|
-
"test:coverage": "tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '
|
|
2951
|
-
"test:coverage:html": "tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '
|
|
2896
|
+
test: "tsc --noEmit && node --import tsx --test --test-reporter=spec '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
2897
|
+
"test:ci": "node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
2898
|
+
"test:coverage": "tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 node --experimental-default-config-file --import tsx --test --experimental-test-coverage '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
2899
|
+
"test:coverage:html": "tsc --noEmit && SKILL_MAP_SKIP_BENCHMARK=1 c8 node --import tsx --test '__tests__/**/*.spec.ts' 'kernel/**/__tests__/**/*.spec.ts' 'cli/**/__tests__/**/*.spec.ts' 'server/**/__tests__/**/*.spec.ts' 'plugins/**/__tests__/**/*.spec.ts' 'core/**/__tests__/**/*.spec.ts' 'conformance/**/__tests__/**/*.spec.ts'",
|
|
2952
2900
|
clean: "rm -rf dist coverage"
|
|
2953
2901
|
},
|
|
2954
2902
|
dependencies: {
|
|
@@ -3237,15 +3185,13 @@ ${footer}
|
|
|
3237
3185
|
`);
|
|
3238
3186
|
}
|
|
3239
3187
|
|
|
3240
|
-
//
|
|
3188
|
+
// plugins/core/hooks/update-check/index.ts
|
|
3241
3189
|
var updateCheckHook = {
|
|
3242
3190
|
id: "update-check",
|
|
3243
3191
|
pluginId: "core",
|
|
3244
3192
|
kind: "hook",
|
|
3245
3193
|
version: "1.0.0",
|
|
3246
3194
|
description: "Checks daily for a newer skill-map version on npm. Shows an `update available` banner when one is found.",
|
|
3247
|
-
stability: "stable",
|
|
3248
|
-
mode: "deterministic",
|
|
3249
3195
|
triggers: ["boot"],
|
|
3250
3196
|
async on(ctx) {
|
|
3251
3197
|
const payload = ctx.event.data ?? {};
|
|
@@ -3259,14 +3205,41 @@ var updateCheckHook = {
|
|
|
3259
3205
|
}
|
|
3260
3206
|
};
|
|
3261
3207
|
|
|
3262
|
-
//
|
|
3208
|
+
// plugins/built-ins.ts
|
|
3209
|
+
var claudeProvider2 = { ...claudeProvider, pluginId: "claude" };
|
|
3210
|
+
var geminiProvider2 = { ...geminiProvider, pluginId: "gemini" };
|
|
3211
|
+
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills" };
|
|
3212
|
+
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core" };
|
|
3213
|
+
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core" };
|
|
3214
|
+
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "core" };
|
|
3215
|
+
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core" };
|
|
3216
|
+
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core" };
|
|
3217
|
+
var slashExtractor2 = { ...slashExtractor, pluginId: "core" };
|
|
3218
|
+
var toolsCountExtractor2 = { ...toolsCountExtractor, pluginId: "core" };
|
|
3219
|
+
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core" };
|
|
3220
|
+
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core" };
|
|
3221
|
+
var brokenRefAnalyzer2 = { ...brokenRefAnalyzer, pluginId: "core" };
|
|
3222
|
+
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core" };
|
|
3223
|
+
var jobOrphanFileAnalyzer2 = { ...jobOrphanFileAnalyzer, pluginId: "core" };
|
|
3224
|
+
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core" };
|
|
3225
|
+
var linkCountsAnalyzer2 = { ...linkCountsAnalyzer, pluginId: "core" };
|
|
3226
|
+
var stabilityAnalyzer2 = { ...stabilityAnalyzer, pluginId: "core" };
|
|
3227
|
+
var supersededAnalyzer2 = { ...supersededAnalyzer, pluginId: "core" };
|
|
3228
|
+
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core" };
|
|
3229
|
+
var unknownFieldAnalyzer2 = { ...unknownFieldAnalyzer, pluginId: "core" };
|
|
3230
|
+
var validateAllAnalyzer2 = { ...validateAllAnalyzer, pluginId: "core" };
|
|
3231
|
+
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core" };
|
|
3232
|
+
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core" };
|
|
3233
|
+
var bumpAction2 = { ...bumpAction, pluginId: "core" };
|
|
3234
|
+
var markSupersededAction2 = { ...markSupersededAction, pluginId: "core" };
|
|
3235
|
+
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core" };
|
|
3263
3236
|
var builtInBundles = [
|
|
3264
3237
|
{
|
|
3265
3238
|
id: "claude",
|
|
3266
3239
|
granularity: "bundle",
|
|
3267
3240
|
description: "Claude Code platform integration. Classifies files under `.claude/{agents,commands,skills}` and parses Claude-flavored frontmatter.",
|
|
3268
3241
|
extensions: [
|
|
3269
|
-
|
|
3242
|
+
claudeProvider2
|
|
3270
3243
|
]
|
|
3271
3244
|
},
|
|
3272
3245
|
{
|
|
@@ -3274,7 +3247,7 @@ var builtInBundles = [
|
|
|
3274
3247
|
granularity: "bundle",
|
|
3275
3248
|
description: "Gemini CLI platform integration. Classifies files under `.gemini/{agents,skills}` and parses Gemini-flavored frontmatter.",
|
|
3276
3249
|
extensions: [
|
|
3277
|
-
|
|
3250
|
+
geminiProvider2
|
|
3278
3251
|
]
|
|
3279
3252
|
},
|
|
3280
3253
|
{
|
|
@@ -3282,7 +3255,7 @@ var builtInBundles = [
|
|
|
3282
3255
|
granularity: "bundle",
|
|
3283
3256
|
description: "Agent Skills open standard. Vendor-neutral path `.agents/skills/<name>/SKILL.md` (Anthropic, OpenAI, Google). See agentskills.io.",
|
|
3284
3257
|
extensions: [
|
|
3285
|
-
|
|
3258
|
+
agentSkillsProvider2
|
|
3286
3259
|
]
|
|
3287
3260
|
},
|
|
3288
3261
|
{
|
|
@@ -3290,39 +3263,30 @@ var builtInBundles = [
|
|
|
3290
3263
|
granularity: "extension",
|
|
3291
3264
|
description: "Core extensions shared across providers: extractors, analyzers, formatters, the bump action, and the universal `.md` fallback Provider.",
|
|
3292
3265
|
extensions: [
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
unknownFieldAnalyzer,
|
|
3318
|
-
contributionOrphanAnalyzer,
|
|
3319
|
-
asciiFormatter,
|
|
3320
|
-
jsonFormatter,
|
|
3321
|
-
validateAllAnalyzer,
|
|
3322
|
-
linkCountsAnalyzer,
|
|
3323
|
-
bumpAction,
|
|
3324
|
-
markSupersededAction,
|
|
3325
|
-
updateCheckHook
|
|
3266
|
+
coreMarkdownProvider2,
|
|
3267
|
+
annotationsExtractor2,
|
|
3268
|
+
atDirectiveExtractor2,
|
|
3269
|
+
externalUrlCounterExtractor2,
|
|
3270
|
+
markdownLinkExtractor2,
|
|
3271
|
+
slashExtractor2,
|
|
3272
|
+
toolsCountExtractor2,
|
|
3273
|
+
annotationOrphanAnalyzer2,
|
|
3274
|
+
annotationStaleAnalyzer2,
|
|
3275
|
+
brokenRefAnalyzer2,
|
|
3276
|
+
contributionOrphanAnalyzer2,
|
|
3277
|
+
jobOrphanFileAnalyzer2,
|
|
3278
|
+
linkConflictAnalyzer2,
|
|
3279
|
+
linkCountsAnalyzer2,
|
|
3280
|
+
stabilityAnalyzer2,
|
|
3281
|
+
supersededAnalyzer2,
|
|
3282
|
+
triggerCollisionAnalyzer2,
|
|
3283
|
+
unknownFieldAnalyzer2,
|
|
3284
|
+
validateAllAnalyzer2,
|
|
3285
|
+
asciiFormatter2,
|
|
3286
|
+
jsonFormatter2,
|
|
3287
|
+
bumpAction2,
|
|
3288
|
+
markSupersededAction2,
|
|
3289
|
+
updateCheckHook2
|
|
3326
3290
|
]
|
|
3327
3291
|
}
|
|
3328
3292
|
];
|
|
@@ -3331,8 +3295,8 @@ function builtIns() {
|
|
|
3331
3295
|
providers: [],
|
|
3332
3296
|
extractors: [],
|
|
3333
3297
|
analyzers: [],
|
|
3334
|
-
actions: [],
|
|
3335
3298
|
formatters: [],
|
|
3299
|
+
actions: [],
|
|
3336
3300
|
hooks: []
|
|
3337
3301
|
};
|
|
3338
3302
|
for (const bundle of builtInBundles) {
|
|
@@ -3356,8 +3320,8 @@ function bucketBuiltIn(ext, out) {
|
|
|
3356
3320
|
provider: out.providers,
|
|
3357
3321
|
extractor: out.extractors,
|
|
3358
3322
|
analyzer: out.analyzers,
|
|
3359
|
-
action: out.actions,
|
|
3360
3323
|
formatter: out.formatters,
|
|
3324
|
+
action: out.actions,
|
|
3361
3325
|
hook: out.hooks
|
|
3362
3326
|
});
|
|
3363
3327
|
}
|
|
@@ -3366,12 +3330,9 @@ function toExtensionRow(x) {
|
|
|
3366
3330
|
id: x.id,
|
|
3367
3331
|
pluginId: x.pluginId,
|
|
3368
3332
|
kind: x.kind,
|
|
3369
|
-
version: x.version
|
|
3333
|
+
version: x.version,
|
|
3334
|
+
description: x.description ?? ""
|
|
3370
3335
|
};
|
|
3371
|
-
if (x.description !== void 0) row.description = x.description;
|
|
3372
|
-
if (x.stability !== void 0) row.stability = x.stability;
|
|
3373
|
-
if (x.preconditions !== void 0) row.preconditions = x.preconditions;
|
|
3374
|
-
if (x.entry !== void 0) row.entry = x.entry;
|
|
3375
3336
|
return row;
|
|
3376
3337
|
}
|
|
3377
3338
|
|
|
@@ -4131,14 +4092,14 @@ function readJsonObjectOrEmpty(path) {
|
|
|
4131
4092
|
}
|
|
4132
4093
|
return {};
|
|
4133
4094
|
}
|
|
4134
|
-
function writeFileAtomicExclusive(path, content) {
|
|
4095
|
+
function writeFileAtomicExclusive(path, content, mode = 384) {
|
|
4135
4096
|
const tmp = `${path}.tmp.${process.pid}.${randomBytes(8).toString("hex")}`;
|
|
4136
4097
|
let fd = null;
|
|
4137
4098
|
try {
|
|
4138
4099
|
fd = openSync(
|
|
4139
4100
|
tmp,
|
|
4140
4101
|
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_NOFOLLOW,
|
|
4141
|
-
|
|
4102
|
+
mode
|
|
4142
4103
|
);
|
|
4143
4104
|
writeSync(fd, content);
|
|
4144
4105
|
closeSync(fd);
|
|
@@ -7124,7 +7085,8 @@ function invokeBumpFor(node, absPath, force) {
|
|
|
7124
7085
|
node,
|
|
7125
7086
|
nodeAbsolutePath: absPath,
|
|
7126
7087
|
invoker: "cli",
|
|
7127
|
-
now: () => /* @__PURE__ */ new Date()
|
|
7088
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
7089
|
+
settings: {}
|
|
7128
7090
|
});
|
|
7129
7091
|
}
|
|
7130
7092
|
|
|
@@ -7644,8 +7606,8 @@ var PLUGIN_LOADER_TEXTS = {
|
|
|
7644
7606
|
|
|
7645
7607
|
// kernel/adapters/plugin-loader/index.ts
|
|
7646
7608
|
import { createRequire as createRequire5 } from "module";
|
|
7647
|
-
import { existsSync as
|
|
7648
|
-
import { join as
|
|
7609
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
7610
|
+
import { join as join8, resolve as resolve16 } from "path";
|
|
7649
7611
|
import { pathToFileURL } from "url";
|
|
7650
7612
|
import semver from "semver";
|
|
7651
7613
|
|
|
@@ -7682,7 +7644,7 @@ function applyIdCollisions(plugins) {
|
|
|
7682
7644
|
const buckets = /* @__PURE__ */ new Map();
|
|
7683
7645
|
for (const p of plugins) {
|
|
7684
7646
|
if (!p.manifest) continue;
|
|
7685
|
-
const id = p.
|
|
7647
|
+
const id = p.id;
|
|
7686
7648
|
const bucket = buckets.get(id);
|
|
7687
7649
|
if (bucket) bucket.push(p);
|
|
7688
7650
|
else buckets.set(id, [p]);
|
|
@@ -7733,39 +7695,22 @@ function extractDefault(mod) {
|
|
|
7733
7695
|
if (!isRecord(mod)) return mod;
|
|
7734
7696
|
return "default" in mod ? mod["default"] : mod;
|
|
7735
7697
|
}
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
for (const [k, v] of Object.entries(input)) {
|
|
7740
|
-
if (typeof v === "function") continue;
|
|
7741
|
-
if (k === "pluginId") continue;
|
|
7742
|
-
if (k === "kinds" && isRecord(v)) {
|
|
7743
|
-
out[k] = stripKindsRuntimeFields(v);
|
|
7744
|
-
continue;
|
|
7745
|
-
}
|
|
7746
|
-
out[k] = v;
|
|
7747
|
-
}
|
|
7748
|
-
return out;
|
|
7749
|
-
}
|
|
7750
|
-
function stripKindsRuntimeFields(kinds) {
|
|
7698
|
+
var LOADER_INJECTED_KEYS = /* @__PURE__ */ new Set(["pluginId", "id", "kind", "kinds", "formatId"]);
|
|
7699
|
+
function stripFunctionsAndPluginId(input) {
|
|
7700
|
+
if (!isRecord(input)) return input;
|
|
7751
7701
|
const out = {};
|
|
7752
|
-
for (const [
|
|
7753
|
-
if (
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
}
|
|
7757
|
-
const cleaned = {};
|
|
7758
|
-
for (const [k, v] of Object.entries(entry)) {
|
|
7759
|
-
if (k === "schemaJson") continue;
|
|
7760
|
-
if (typeof v === "function") continue;
|
|
7761
|
-
cleaned[k] = v;
|
|
7762
|
-
}
|
|
7763
|
-
out[kind] = cleaned;
|
|
7702
|
+
for (const [k, v] of Object.entries(input)) {
|
|
7703
|
+
if (typeof v === "function") continue;
|
|
7704
|
+
if (LOADER_INJECTED_KEYS.has(k)) continue;
|
|
7705
|
+
out[k] = v;
|
|
7764
7706
|
}
|
|
7765
7707
|
return out;
|
|
7766
7708
|
}
|
|
7767
7709
|
|
|
7768
7710
|
// kernel/adapters/plugin-loader/validation.ts
|
|
7711
|
+
import * as nodeFs from "fs";
|
|
7712
|
+
import { existsSync as existsSync11 } from "fs";
|
|
7713
|
+
import { dirname as dirname9, join as join7 } from "path";
|
|
7769
7714
|
import { Ajv2020 as Ajv20205 } from "ajv/dist/2020.js";
|
|
7770
7715
|
|
|
7771
7716
|
// kernel/extensions/hook.ts
|
|
@@ -7793,76 +7738,73 @@ var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
|
7793
7738
|
]);
|
|
7794
7739
|
var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
|
|
7795
7740
|
var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
|
|
7796
|
-
function validateAnnotationContributions(pluginPath, manifest, relEntry, manifestView) {
|
|
7741
|
+
function validateAnnotationContributions(pluginPath, pluginId, manifest, relEntry, manifestView) {
|
|
7797
7742
|
if (!isRecord(manifestView)) return null;
|
|
7798
|
-
const raw = manifestView["
|
|
7743
|
+
const raw = manifestView["annotation"];
|
|
7799
7744
|
if (raw === void 0) return null;
|
|
7800
7745
|
if (!isRecord(raw)) return null;
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
}
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
manifest
|
|
7853
|
-
};
|
|
7854
|
-
}
|
|
7746
|
+
const location = raw["location"] ?? "namespaced";
|
|
7747
|
+
const ownership = raw["ownership"] ?? "shared";
|
|
7748
|
+
if (location === "root" && ownership !== "exclusive") {
|
|
7749
|
+
return {
|
|
7750
|
+
...fail(
|
|
7751
|
+
pluginPath,
|
|
7752
|
+
pluginId,
|
|
7753
|
+
"invalid-manifest",
|
|
7754
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestRootSharedAnnotation, {
|
|
7755
|
+
relEntry,
|
|
7756
|
+
key: "<annotation>",
|
|
7757
|
+
ownership
|
|
7758
|
+
})
|
|
7759
|
+
),
|
|
7760
|
+
manifest
|
|
7761
|
+
};
|
|
7762
|
+
}
|
|
7763
|
+
const schema = raw["schema"];
|
|
7764
|
+
if (!isRecord(schema)) {
|
|
7765
|
+
return {
|
|
7766
|
+
...fail(
|
|
7767
|
+
pluginPath,
|
|
7768
|
+
pluginId,
|
|
7769
|
+
"invalid-manifest",
|
|
7770
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestAnnotationSchemaCompile, {
|
|
7771
|
+
relEntry,
|
|
7772
|
+
key: "<annotation>",
|
|
7773
|
+
errDescription: "schema must be an object literal"
|
|
7774
|
+
})
|
|
7775
|
+
),
|
|
7776
|
+
manifest
|
|
7777
|
+
};
|
|
7778
|
+
}
|
|
7779
|
+
try {
|
|
7780
|
+
const ajv = new Ajv20205({ strict: false, allErrors: true, allowUnionTypes: true });
|
|
7781
|
+
applyAjvFormats(ajv);
|
|
7782
|
+
ajv.compile(schema);
|
|
7783
|
+
} catch (err) {
|
|
7784
|
+
return {
|
|
7785
|
+
...fail(
|
|
7786
|
+
pluginPath,
|
|
7787
|
+
pluginId,
|
|
7788
|
+
"invalid-manifest",
|
|
7789
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestAnnotationSchemaCompile, {
|
|
7790
|
+
relEntry,
|
|
7791
|
+
key: "<annotation>",
|
|
7792
|
+
errDescription: describe(err)
|
|
7793
|
+
})
|
|
7794
|
+
),
|
|
7795
|
+
manifest
|
|
7796
|
+
};
|
|
7855
7797
|
}
|
|
7856
7798
|
return null;
|
|
7857
7799
|
}
|
|
7858
|
-
function validateHookTriggers(pluginPath, manifest, relEntry, exported, manifestView) {
|
|
7800
|
+
function validateHookTriggers(pluginPath, pluginId, manifest, relEntry, exported, manifestView) {
|
|
7859
7801
|
const triggers = manifestView["triggers"];
|
|
7860
7802
|
const hookId = exported["id"] ?? "?";
|
|
7861
7803
|
if (!Array.isArray(triggers) || triggers.length === 0) {
|
|
7862
7804
|
return {
|
|
7863
7805
|
...fail(
|
|
7864
7806
|
pluginPath,
|
|
7865
|
-
|
|
7807
|
+
pluginId,
|
|
7866
7808
|
"invalid-manifest",
|
|
7867
7809
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestHookEmptyTriggers, { hookId })
|
|
7868
7810
|
),
|
|
@@ -7874,7 +7816,7 @@ function validateHookTriggers(pluginPath, manifest, relEntry, exported, manifest
|
|
|
7874
7816
|
return {
|
|
7875
7817
|
...fail(
|
|
7876
7818
|
pluginPath,
|
|
7877
|
-
|
|
7819
|
+
pluginId,
|
|
7878
7820
|
"invalid-manifest",
|
|
7879
7821
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestHookUnknownTrigger, {
|
|
7880
7822
|
hookId,
|
|
@@ -7888,9 +7830,135 @@ function validateHookTriggers(pluginPath, manifest, relEntry, exported, manifest
|
|
|
7888
7830
|
}
|
|
7889
7831
|
return null;
|
|
7890
7832
|
}
|
|
7833
|
+
function validateActionFileConventions(pluginPath, pluginId, manifest, relEntry, entryAbsPath, manifestView) {
|
|
7834
|
+
const actionDir = dirname9(entryAbsPath);
|
|
7835
|
+
const reportSchemaPath = join7(actionDir, "report.schema.json");
|
|
7836
|
+
const promptPath = join7(actionDir, "prompt.md");
|
|
7837
|
+
const mode = isRecord(manifestView) && typeof manifestView["mode"] === "string" ? manifestView["mode"] : "deterministic";
|
|
7838
|
+
if (!existsSync11(reportSchemaPath)) {
|
|
7839
|
+
return {
|
|
7840
|
+
...fail(
|
|
7841
|
+
pluginPath,
|
|
7842
|
+
pluginId,
|
|
7843
|
+
"load-error",
|
|
7844
|
+
`Action at \`${relEntry}\` is missing \`report.schema.json\` in its folder (structure-as-truth: every Action carries a report schema by convention).`
|
|
7845
|
+
),
|
|
7846
|
+
manifest
|
|
7847
|
+
};
|
|
7848
|
+
}
|
|
7849
|
+
const promptExists = existsSync11(promptPath);
|
|
7850
|
+
if (mode === "probabilistic" && !promptExists) {
|
|
7851
|
+
return {
|
|
7852
|
+
...fail(
|
|
7853
|
+
pluginPath,
|
|
7854
|
+
pluginId,
|
|
7855
|
+
"load-error",
|
|
7856
|
+
`Probabilistic Action at \`${relEntry}\` is missing \`prompt.md\` in its folder (structure-as-truth: probabilistic Actions carry a prompt template by convention).`
|
|
7857
|
+
),
|
|
7858
|
+
manifest
|
|
7859
|
+
};
|
|
7860
|
+
}
|
|
7861
|
+
if (mode === "deterministic" && promptExists) {
|
|
7862
|
+
return {
|
|
7863
|
+
...fail(
|
|
7864
|
+
pluginPath,
|
|
7865
|
+
pluginId,
|
|
7866
|
+
"invalid-manifest",
|
|
7867
|
+
`Deterministic Action at \`${relEntry}\` carries an unexpected \`prompt.md\` (delete the file or switch \`mode\` to \`'probabilistic'\`).`
|
|
7868
|
+
),
|
|
7869
|
+
manifest
|
|
7870
|
+
};
|
|
7871
|
+
}
|
|
7872
|
+
return null;
|
|
7873
|
+
}
|
|
7874
|
+
function discoverProviderKinds(pluginPath, pluginId, manifest, relEntry, validatorForKind) {
|
|
7875
|
+
const kindsRoot = join7(pluginPath, "kinds");
|
|
7876
|
+
let entries;
|
|
7877
|
+
try {
|
|
7878
|
+
entries = nodeFs.readdirSync(kindsRoot);
|
|
7879
|
+
} catch {
|
|
7880
|
+
return { ok: true, kinds: {} };
|
|
7881
|
+
}
|
|
7882
|
+
const out = {};
|
|
7883
|
+
for (const entry of entries.sort()) {
|
|
7884
|
+
if (entry.startsWith(".")) continue;
|
|
7885
|
+
const kindDir = join7(kindsRoot, entry);
|
|
7886
|
+
if (!isDirectorySafe(kindDir, nodeFs.statSync)) continue;
|
|
7887
|
+
const result = loadOneProviderKind({
|
|
7888
|
+
pluginPath,
|
|
7889
|
+
pluginId,
|
|
7890
|
+
manifest,
|
|
7891
|
+
relEntry,
|
|
7892
|
+
entry,
|
|
7893
|
+
kindDir,
|
|
7894
|
+
validatorForKind
|
|
7895
|
+
});
|
|
7896
|
+
if (!result.ok) return result;
|
|
7897
|
+
out[entry] = result.kind;
|
|
7898
|
+
}
|
|
7899
|
+
return { ok: true, kinds: out };
|
|
7900
|
+
}
|
|
7901
|
+
function loadOneProviderKind(opts) {
|
|
7902
|
+
const schemaJson = readJsonFile(join7(opts.kindDir, "schema.json"));
|
|
7903
|
+
if ("error" in schemaJson) {
|
|
7904
|
+
return providerKindFailure(opts, "load-error", "schema.json", schemaJson.error);
|
|
7905
|
+
}
|
|
7906
|
+
const kindJson = readJsonFile(join7(opts.kindDir, "kind.json"));
|
|
7907
|
+
if ("error" in kindJson) {
|
|
7908
|
+
return providerKindFailure(opts, "invalid-manifest", "kind.json", kindJson.error);
|
|
7909
|
+
}
|
|
7910
|
+
const validation = opts.validatorForKind(kindJson.value);
|
|
7911
|
+
if (!validation.ok) {
|
|
7912
|
+
return {
|
|
7913
|
+
ok: false,
|
|
7914
|
+
failure: {
|
|
7915
|
+
...fail(
|
|
7916
|
+
opts.pluginPath,
|
|
7917
|
+
opts.pluginId,
|
|
7918
|
+
"invalid-manifest",
|
|
7919
|
+
`Provider kind \`${opts.entry}\` (declared at \`${opts.relEntry}\`) failed validation in \`kinds/${opts.entry}/kind.json\`: ${validation.errors}. See spec/schemas/extensions/provider-kind.schema.json.`
|
|
7920
|
+
),
|
|
7921
|
+
manifest: opts.manifest
|
|
7922
|
+
}
|
|
7923
|
+
};
|
|
7924
|
+
}
|
|
7925
|
+
const ui = isRecord(kindJson.value) ? kindJson.value["ui"] : void 0;
|
|
7926
|
+
return {
|
|
7927
|
+
ok: true,
|
|
7928
|
+
kind: { schema: `./kinds/${opts.entry}/schema.json`, schemaJson: schemaJson.value, ui }
|
|
7929
|
+
};
|
|
7930
|
+
}
|
|
7931
|
+
function readJsonFile(path) {
|
|
7932
|
+
try {
|
|
7933
|
+
return { value: JSON.parse(nodeFs.readFileSync(path, "utf8")) };
|
|
7934
|
+
} catch (err) {
|
|
7935
|
+
return { error: describe(err) };
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
function providerKindFailure(opts, status, fileName, errDescription) {
|
|
7939
|
+
return {
|
|
7940
|
+
ok: false,
|
|
7941
|
+
failure: {
|
|
7942
|
+
...fail(
|
|
7943
|
+
opts.pluginPath,
|
|
7944
|
+
opts.pluginId,
|
|
7945
|
+
status,
|
|
7946
|
+
`Provider kind \`${opts.entry}\` (declared at \`${opts.relEntry}\`) is missing or has an unparseable \`kinds/${opts.entry}/${fileName}\` (${errDescription}).`
|
|
7947
|
+
),
|
|
7948
|
+
manifest: opts.manifest
|
|
7949
|
+
}
|
|
7950
|
+
};
|
|
7951
|
+
}
|
|
7952
|
+
function isDirectorySafe(path, statSync11) {
|
|
7953
|
+
try {
|
|
7954
|
+
return statSync11(path).isDirectory();
|
|
7955
|
+
} catch {
|
|
7956
|
+
return false;
|
|
7957
|
+
}
|
|
7958
|
+
}
|
|
7891
7959
|
|
|
7892
7960
|
// kernel/adapters/plugin-loader/storage-schemas.ts
|
|
7893
|
-
import { readFileSync as
|
|
7961
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
7894
7962
|
import { resolve as resolve15 } from "path";
|
|
7895
7963
|
import { Ajv2020 as Ajv20206 } from "ajv/dist/2020.js";
|
|
7896
7964
|
|
|
@@ -7898,7 +7966,7 @@ import { Ajv2020 as Ajv20206 } from "ajv/dist/2020.js";
|
|
|
7898
7966
|
var KV_SCHEMA_KEY = "__kv__";
|
|
7899
7967
|
|
|
7900
7968
|
// kernel/adapters/plugin-loader/storage-schemas.ts
|
|
7901
|
-
function loadStorageSchemas(pluginPath, manifest) {
|
|
7969
|
+
function loadStorageSchemas(pluginPath, pluginId, manifest) {
|
|
7902
7970
|
const storage = manifest.storage;
|
|
7903
7971
|
if (!storage) return { ok: true };
|
|
7904
7972
|
if (storage.mode === "kv") {
|
|
@@ -7908,7 +7976,7 @@ function loadStorageSchemas(pluginPath, manifest) {
|
|
|
7908
7976
|
const reason = tx(
|
|
7909
7977
|
compiled.phase === "read" ? PLUGIN_LOADER_TEXTS.loadErrorStorageKvSchemaRead : PLUGIN_LOADER_TEXTS.loadErrorStorageKvSchemaCompile,
|
|
7910
7978
|
{
|
|
7911
|
-
pluginId
|
|
7979
|
+
pluginId,
|
|
7912
7980
|
schemaPath: storage.schema,
|
|
7913
7981
|
errDescription: compiled.errDescription
|
|
7914
7982
|
}
|
|
@@ -7935,7 +8003,7 @@ function loadStorageSchemas(pluginPath, manifest) {
|
|
|
7935
8003
|
const reason = tx(
|
|
7936
8004
|
compiled.phase === "read" ? PLUGIN_LOADER_TEXTS.loadErrorStorageSchemaRead : PLUGIN_LOADER_TEXTS.loadErrorStorageSchemaCompile,
|
|
7937
8005
|
{
|
|
7938
|
-
pluginId
|
|
8006
|
+
pluginId,
|
|
7939
8007
|
table,
|
|
7940
8008
|
schemaPath: relPath,
|
|
7941
8009
|
errDescription: compiled.errDescription
|
|
@@ -7958,7 +8026,7 @@ function compilePluginSchema(pluginPath, relPath) {
|
|
|
7958
8026
|
const abs = resolve15(pluginPath, relPath);
|
|
7959
8027
|
let raw;
|
|
7960
8028
|
try {
|
|
7961
|
-
raw = JSON.parse(
|
|
8029
|
+
raw = JSON.parse(readFileSync11(abs, "utf8"));
|
|
7962
8030
|
} catch (err) {
|
|
7963
8031
|
return { ok: false, phase: "read", errDescription: describe(err) };
|
|
7964
8032
|
}
|
|
@@ -7992,11 +8060,11 @@ var PluginLoader = class {
|
|
|
7992
8060
|
discoverPaths() {
|
|
7993
8061
|
const out = [];
|
|
7994
8062
|
for (const root of this.#options.searchPaths) {
|
|
7995
|
-
if (!
|
|
7996
|
-
for (const entry of
|
|
8063
|
+
if (!existsSync12(root)) continue;
|
|
8064
|
+
for (const entry of readdirSync4(root, { withFileTypes: true })) {
|
|
7997
8065
|
if (!entry.isDirectory()) continue;
|
|
7998
|
-
const candidate =
|
|
7999
|
-
if (
|
|
8066
|
+
const candidate = join8(root, entry.name);
|
|
8067
|
+
if (existsSync12(join8(candidate, "plugin.json"))) {
|
|
8000
8068
|
out.push(resolve16(candidate));
|
|
8001
8069
|
}
|
|
8002
8070
|
}
|
|
@@ -8006,7 +8074,7 @@ var PluginLoader = class {
|
|
|
8006
8074
|
/**
|
|
8007
8075
|
* Full pass, discover every plugin, attempt to load each, then apply
|
|
8008
8076
|
* the cross-root id-collision pass over the results. Two plugins that
|
|
8009
|
-
* survived their individual load with the same `
|
|
8077
|
+
* survived their individual load with the same `pluginId` both get
|
|
8010
8078
|
* downgraded to status `id-collision` (no precedence, the spec is
|
|
8011
8079
|
* explicit that "no extension is privileged"). Plugins that already
|
|
8012
8080
|
* failed their individual load (`invalid-manifest` /
|
|
@@ -8026,61 +8094,69 @@ var PluginLoader = class {
|
|
|
8026
8094
|
/**
|
|
8027
8095
|
* Load a single plugin from its directory. Never throws, a failure is
|
|
8028
8096
|
* reported via the returned status.
|
|
8097
|
+
*
|
|
8098
|
+
* Cyclomatic count covers the four sequential phases (manifest parse,
|
|
8099
|
+
* enabled resolution, per-extension load loop, storage output-schemas
|
|
8100
|
+
* compile) plus their failure short-circuits. Splitting each phase
|
|
8101
|
+
* into a helper would scatter the return-on-failure pattern without
|
|
8102
|
+
* making the orchestration clearer.
|
|
8029
8103
|
*/
|
|
8030
8104
|
// eslint-disable-next-line complexity
|
|
8031
8105
|
async loadOne(pluginPath) {
|
|
8032
|
-
const
|
|
8106
|
+
const pluginId = pathId(pluginPath);
|
|
8107
|
+
const manifestResult = this.#parseAndValidateManifest(pluginPath, pluginId);
|
|
8033
8108
|
if (!manifestResult.ok) return manifestResult.failure;
|
|
8034
8109
|
const manifest = manifestResult.manifest;
|
|
8035
|
-
|
|
8110
|
+
const granularity = manifest.granularity ?? "extension";
|
|
8111
|
+
if (this.#options.resolveEnabled && !this.#options.resolveEnabled(pluginId)) {
|
|
8036
8112
|
return {
|
|
8037
8113
|
path: pluginPath,
|
|
8038
|
-
id:
|
|
8114
|
+
id: pluginId,
|
|
8039
8115
|
status: "disabled",
|
|
8040
8116
|
manifest,
|
|
8041
|
-
granularity
|
|
8117
|
+
granularity,
|
|
8042
8118
|
reason: PLUGIN_LOADER_TEXTS.disabledByConfig
|
|
8043
8119
|
};
|
|
8044
8120
|
}
|
|
8045
8121
|
const loaded = [];
|
|
8046
|
-
for (const relEntry of
|
|
8047
|
-
const result = await this.#loadAndValidateExtensionEntry(pluginPath, manifest, relEntry);
|
|
8122
|
+
for (const relEntry of discoverExtensionEntries(pluginPath)) {
|
|
8123
|
+
const result = await this.#loadAndValidateExtensionEntry(pluginPath, pluginId, manifest, relEntry);
|
|
8048
8124
|
if (!result.ok) return result.failure;
|
|
8049
8125
|
loaded.push(result.extension);
|
|
8050
8126
|
}
|
|
8051
|
-
const storageSchemasResult = loadStorageSchemas(pluginPath, manifest);
|
|
8127
|
+
const storageSchemasResult = loadStorageSchemas(pluginPath, pluginId, manifest);
|
|
8052
8128
|
if (!storageSchemasResult.ok) {
|
|
8053
8129
|
return {
|
|
8054
|
-
...fail(pluginPath,
|
|
8130
|
+
...fail(pluginPath, pluginId, "load-error", storageSchemasResult.reason),
|
|
8055
8131
|
manifest
|
|
8056
8132
|
};
|
|
8057
8133
|
}
|
|
8058
8134
|
return {
|
|
8059
8135
|
path: pluginPath,
|
|
8060
|
-
id:
|
|
8136
|
+
id: pluginId,
|
|
8061
8137
|
status: "enabled",
|
|
8062
8138
|
manifest,
|
|
8063
|
-
granularity
|
|
8139
|
+
granularity,
|
|
8064
8140
|
extensions: loaded,
|
|
8065
8141
|
...storageSchemasResult.schemas ? { storageSchemas: storageSchemasResult.schemas } : {}
|
|
8066
8142
|
};
|
|
8067
8143
|
}
|
|
8068
8144
|
/**
|
|
8069
8145
|
* Phase 1 of `loadOne`, read `plugin.json`, AJV-validate the manifest,
|
|
8070
|
-
* enforce the directory-name ==
|
|
8146
|
+
* enforce the directory-name == pluginId structural rule, and check
|
|
8071
8147
|
* specCompat (range syntax + satisfies the installed spec version).
|
|
8072
8148
|
* Returns either the validated manifest or an `IDiscoveredPlugin` with
|
|
8073
8149
|
* the appropriate failure status.
|
|
8074
8150
|
*/
|
|
8075
|
-
#parseAndValidateManifest(pluginPath) {
|
|
8076
|
-
const manifestPath =
|
|
8151
|
+
#parseAndValidateManifest(pluginPath, pluginId) {
|
|
8152
|
+
const manifestPath = join8(pluginPath, "plugin.json");
|
|
8077
8153
|
let raw;
|
|
8078
8154
|
try {
|
|
8079
|
-
raw = JSON.parse(
|
|
8155
|
+
raw = JSON.parse(readFileSync12(manifestPath, "utf8"));
|
|
8080
8156
|
} catch (err) {
|
|
8081
8157
|
return { ok: false, failure: fail(
|
|
8082
8158
|
pluginPath,
|
|
8083
|
-
|
|
8159
|
+
pluginId,
|
|
8084
8160
|
"invalid-manifest",
|
|
8085
8161
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestJsonParse, {
|
|
8086
8162
|
manifestPath,
|
|
@@ -8092,7 +8168,7 @@ var PluginLoader = class {
|
|
|
8092
8168
|
if (!manifestResult.ok) {
|
|
8093
8169
|
return { ok: false, failure: fail(
|
|
8094
8170
|
pluginPath,
|
|
8095
|
-
|
|
8171
|
+
pluginId,
|
|
8096
8172
|
"invalid-manifest",
|
|
8097
8173
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestAjv, {
|
|
8098
8174
|
manifestPath,
|
|
@@ -8101,26 +8177,11 @@ var PluginLoader = class {
|
|
|
8101
8177
|
) };
|
|
8102
8178
|
}
|
|
8103
8179
|
const manifest = manifestResult.data;
|
|
8104
|
-
const dirName = pathId(pluginPath);
|
|
8105
|
-
if (dirName !== manifest.id) {
|
|
8106
|
-
return { ok: false, failure: {
|
|
8107
|
-
...fail(
|
|
8108
|
-
pluginPath,
|
|
8109
|
-
manifest.id,
|
|
8110
|
-
"invalid-manifest",
|
|
8111
|
-
tx(PLUGIN_LOADER_TEXTS.invalidManifestDirMismatch, {
|
|
8112
|
-
dirName,
|
|
8113
|
-
manifestId: manifest.id
|
|
8114
|
-
})
|
|
8115
|
-
),
|
|
8116
|
-
manifest
|
|
8117
|
-
} };
|
|
8118
|
-
}
|
|
8119
8180
|
if (!semver.validRange(manifest.specCompat)) {
|
|
8120
8181
|
return { ok: false, failure: {
|
|
8121
8182
|
...fail(
|
|
8122
8183
|
pluginPath,
|
|
8123
|
-
|
|
8184
|
+
pluginId,
|
|
8124
8185
|
"invalid-manifest",
|
|
8125
8186
|
tx(PLUGIN_LOADER_TEXTS.invalidSpecCompat, { specCompat: manifest.specCompat })
|
|
8126
8187
|
),
|
|
@@ -8130,10 +8191,10 @@ var PluginLoader = class {
|
|
|
8130
8191
|
if (!semver.satisfies(this.#options.specVersion, manifest.specCompat, { includePrerelease: true })) {
|
|
8131
8192
|
return { ok: false, failure: {
|
|
8132
8193
|
path: pluginPath,
|
|
8133
|
-
id:
|
|
8194
|
+
id: pluginId,
|
|
8134
8195
|
status: "incompatible-spec",
|
|
8135
8196
|
manifest,
|
|
8136
|
-
granularity: manifest.granularity ?? "
|
|
8197
|
+
granularity: manifest.granularity ?? "extension",
|
|
8137
8198
|
reason: tx(PLUGIN_LOADER_TEXTS.incompatibleSpec, {
|
|
8138
8199
|
installedSpecVersion: this.#options.specVersion,
|
|
8139
8200
|
specCompat: manifest.specCompat
|
|
@@ -8156,12 +8217,12 @@ var PluginLoader = class {
|
|
|
8156
8217
|
// splitting per sub-check would multiply the discriminated-union
|
|
8157
8218
|
// boilerplate without making the validation pipeline clearer.
|
|
8158
8219
|
// eslint-disable-next-line complexity
|
|
8159
|
-
async #loadAndValidateExtensionEntry(pluginPath, manifest, relEntry) {
|
|
8220
|
+
async #loadAndValidateExtensionEntry(pluginPath, pluginId, manifest, relEntry) {
|
|
8160
8221
|
if (!isInsidePlugin(pluginPath, relEntry)) {
|
|
8161
8222
|
return { ok: false, failure: {
|
|
8162
8223
|
...fail(
|
|
8163
8224
|
pluginPath,
|
|
8164
|
-
|
|
8225
|
+
pluginId,
|
|
8165
8226
|
"invalid-manifest",
|
|
8166
8227
|
tx(PLUGIN_LOADER_TEXTS.loadErrorPathEscapesPlugin, { relEntry, pluginPath })
|
|
8167
8228
|
),
|
|
@@ -8169,11 +8230,11 @@ var PluginLoader = class {
|
|
|
8169
8230
|
} };
|
|
8170
8231
|
}
|
|
8171
8232
|
const abs = resolve16(pluginPath, relEntry);
|
|
8172
|
-
if (!
|
|
8233
|
+
if (!existsSync12(abs)) {
|
|
8173
8234
|
return { ok: false, failure: {
|
|
8174
8235
|
...fail(
|
|
8175
8236
|
pluginPath,
|
|
8176
|
-
|
|
8237
|
+
pluginId,
|
|
8177
8238
|
"load-error",
|
|
8178
8239
|
tx(PLUGIN_LOADER_TEXTS.loadErrorFileNotFound, { relEntry, abs })
|
|
8179
8240
|
),
|
|
@@ -8187,7 +8248,7 @@ var PluginLoader = class {
|
|
|
8187
8248
|
return { ok: false, failure: {
|
|
8188
8249
|
...fail(
|
|
8189
8250
|
pluginPath,
|
|
8190
|
-
|
|
8251
|
+
pluginId,
|
|
8191
8252
|
"load-error",
|
|
8192
8253
|
tx(PLUGIN_LOADER_TEXTS.loadErrorImportFailed, {
|
|
8193
8254
|
relEntry,
|
|
@@ -8198,11 +8259,11 @@ var PluginLoader = class {
|
|
|
8198
8259
|
} };
|
|
8199
8260
|
}
|
|
8200
8261
|
const exported = extractDefault(mod);
|
|
8201
|
-
if (!isRecord(exported)
|
|
8262
|
+
if (!isRecord(exported)) {
|
|
8202
8263
|
return { ok: false, failure: {
|
|
8203
8264
|
...fail(
|
|
8204
8265
|
pluginPath,
|
|
8205
|
-
|
|
8266
|
+
pluginId,
|
|
8206
8267
|
"load-error",
|
|
8207
8268
|
tx(PLUGIN_LOADER_TEXTS.loadErrorMissingKind, {
|
|
8208
8269
|
relEntry,
|
|
@@ -8212,16 +8273,32 @@ var PluginLoader = class {
|
|
|
8212
8273
|
manifest
|
|
8213
8274
|
} };
|
|
8214
8275
|
}
|
|
8215
|
-
const
|
|
8216
|
-
|
|
8276
|
+
const [pathKindDir, pathId2] = relEntry.split("/");
|
|
8277
|
+
const kindFromPath = pathKindDir && pathKindDir.endsWith("s") ? pathKindDir.slice(0, -1) : void 0;
|
|
8278
|
+
if (!kindFromPath || !KNOWN_KINDS.has(kindFromPath)) {
|
|
8217
8279
|
return { ok: false, failure: {
|
|
8218
8280
|
...fail(
|
|
8219
8281
|
pluginPath,
|
|
8220
|
-
|
|
8221
|
-
"
|
|
8282
|
+
pluginId,
|
|
8283
|
+
"invalid-manifest",
|
|
8222
8284
|
tx(PLUGIN_LOADER_TEXTS.loadErrorUnknownKind, {
|
|
8223
8285
|
relEntry,
|
|
8224
|
-
kindReceived: String(
|
|
8286
|
+
kindReceived: String(pathKindDir ?? "(missing)"),
|
|
8287
|
+
knownKindsList: KNOWN_KINDS_LIST
|
|
8288
|
+
})
|
|
8289
|
+
),
|
|
8290
|
+
manifest
|
|
8291
|
+
} };
|
|
8292
|
+
}
|
|
8293
|
+
const kind = kindFromPath;
|
|
8294
|
+
if (!pathId2) {
|
|
8295
|
+
return { ok: false, failure: {
|
|
8296
|
+
...fail(
|
|
8297
|
+
pluginPath,
|
|
8298
|
+
pluginId,
|
|
8299
|
+
"invalid-manifest",
|
|
8300
|
+
tx(PLUGIN_LOADER_TEXTS.loadErrorMissingKind, {
|
|
8301
|
+
relEntry,
|
|
8225
8302
|
knownKindsList: KNOWN_KINDS_LIST
|
|
8226
8303
|
})
|
|
8227
8304
|
),
|
|
@@ -8229,16 +8306,16 @@ var PluginLoader = class {
|
|
|
8229
8306
|
} };
|
|
8230
8307
|
}
|
|
8231
8308
|
const declaredPluginId = exported["pluginId"];
|
|
8232
|
-
if (typeof declaredPluginId === "string" && declaredPluginId !==
|
|
8309
|
+
if (typeof declaredPluginId === "string" && declaredPluginId !== pluginId) {
|
|
8233
8310
|
return { ok: false, failure: {
|
|
8234
8311
|
...fail(
|
|
8235
8312
|
pluginPath,
|
|
8236
|
-
|
|
8313
|
+
pluginId,
|
|
8237
8314
|
"invalid-manifest",
|
|
8238
8315
|
tx(PLUGIN_LOADER_TEXTS.loadErrorPluginIdMismatch, {
|
|
8239
8316
|
relEntry,
|
|
8240
8317
|
declared: declaredPluginId,
|
|
8241
|
-
manifestId:
|
|
8318
|
+
manifestId: pluginId
|
|
8242
8319
|
})
|
|
8243
8320
|
),
|
|
8244
8321
|
manifest
|
|
@@ -8246,7 +8323,7 @@ var PluginLoader = class {
|
|
|
8246
8323
|
}
|
|
8247
8324
|
const manifestView = stripFunctionsAndPluginId(exported);
|
|
8248
8325
|
if (kind === "hook") {
|
|
8249
|
-
const hookFailure = validateHookTriggers(pluginPath, manifest, relEntry, exported, manifestView);
|
|
8326
|
+
const hookFailure = validateHookTriggers(pluginPath, pluginId, manifest, relEntry, exported, manifestView);
|
|
8250
8327
|
if (hookFailure) return { ok: false, failure: hookFailure };
|
|
8251
8328
|
}
|
|
8252
8329
|
const extValidator = this.#options.validators.validatorForExtension(kind);
|
|
@@ -8255,7 +8332,7 @@ var PluginLoader = class {
|
|
|
8255
8332
|
return { ok: false, failure: {
|
|
8256
8333
|
...fail(
|
|
8257
8334
|
pluginPath,
|
|
8258
|
-
|
|
8335
|
+
pluginId,
|
|
8259
8336
|
"invalid-manifest",
|
|
8260
8337
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestExtensionShape, { relEntry, kind, errors })
|
|
8261
8338
|
),
|
|
@@ -8264,16 +8341,49 @@ var PluginLoader = class {
|
|
|
8264
8341
|
}
|
|
8265
8342
|
const contribFailure = validateAnnotationContributions(
|
|
8266
8343
|
pluginPath,
|
|
8344
|
+
pluginId,
|
|
8267
8345
|
manifest,
|
|
8268
8346
|
relEntry,
|
|
8269
8347
|
manifestView
|
|
8270
8348
|
);
|
|
8271
8349
|
if (contribFailure) return { ok: false, failure: contribFailure };
|
|
8272
|
-
|
|
8350
|
+
if (kind === "action") {
|
|
8351
|
+
const actionFailure = validateActionFileConventions(
|
|
8352
|
+
pluginPath,
|
|
8353
|
+
pluginId,
|
|
8354
|
+
manifest,
|
|
8355
|
+
relEntry,
|
|
8356
|
+
abs,
|
|
8357
|
+
manifestView
|
|
8358
|
+
);
|
|
8359
|
+
if (actionFailure) return { ok: false, failure: actionFailure };
|
|
8360
|
+
}
|
|
8361
|
+
let discoveredKinds;
|
|
8362
|
+
if (kind === "provider") {
|
|
8363
|
+
const kindsResult = discoverProviderKinds(
|
|
8364
|
+
pluginPath,
|
|
8365
|
+
pluginId,
|
|
8366
|
+
manifest,
|
|
8367
|
+
relEntry,
|
|
8368
|
+
(data) => {
|
|
8369
|
+
const v = this.#options.validators.validate("extension-provider-kind", data);
|
|
8370
|
+
if (v.ok) return { ok: true, errors: "" };
|
|
8371
|
+
return { ok: false, errors: v.errors };
|
|
8372
|
+
}
|
|
8373
|
+
);
|
|
8374
|
+
if (!kindsResult.ok) return { ok: false, failure: kindsResult.failure };
|
|
8375
|
+
if (Object.keys(kindsResult.kinds).length > 0) discoveredKinds = kindsResult.kinds;
|
|
8376
|
+
}
|
|
8377
|
+
const instance = { ...exported, pluginId, id: pathId2, kind };
|
|
8378
|
+
if (kind === "formatter") instance["formatId"] = pathId2;
|
|
8379
|
+
if (kind === "provider" && discoveredKinds) {
|
|
8380
|
+
const inlineKinds = isRecord(exported["kinds"]) ? exported["kinds"] : {};
|
|
8381
|
+
instance["kinds"] = { ...inlineKinds, ...discoveredKinds };
|
|
8382
|
+
}
|
|
8273
8383
|
return { ok: true, extension: {
|
|
8274
8384
|
kind,
|
|
8275
|
-
id:
|
|
8276
|
-
pluginId
|
|
8385
|
+
id: pathId2,
|
|
8386
|
+
pluginId,
|
|
8277
8387
|
version: exported["version"],
|
|
8278
8388
|
entryPath: abs,
|
|
8279
8389
|
module: mod,
|
|
@@ -8281,11 +8391,64 @@ var PluginLoader = class {
|
|
|
8281
8391
|
} };
|
|
8282
8392
|
}
|
|
8283
8393
|
};
|
|
8394
|
+
var KIND_DIR_NAMES = [
|
|
8395
|
+
"providers",
|
|
8396
|
+
"extractors",
|
|
8397
|
+
"analyzers",
|
|
8398
|
+
"actions",
|
|
8399
|
+
"formatters",
|
|
8400
|
+
"hooks"
|
|
8401
|
+
];
|
|
8402
|
+
var INDEX_CANDIDATES = [
|
|
8403
|
+
"index.js",
|
|
8404
|
+
"index.mjs",
|
|
8405
|
+
"index.ts"
|
|
8406
|
+
];
|
|
8407
|
+
function discoverExtensionEntries(pluginPath) {
|
|
8408
|
+
const out = [];
|
|
8409
|
+
for (const kindDir of KIND_DIR_NAMES) {
|
|
8410
|
+
collectKindEntries(pluginPath, kindDir, out);
|
|
8411
|
+
}
|
|
8412
|
+
return out;
|
|
8413
|
+
}
|
|
8414
|
+
function collectKindEntries(pluginPath, kindDir, out) {
|
|
8415
|
+
const kindAbs = resolve16(pluginPath, kindDir);
|
|
8416
|
+
if (!existsSync12(kindAbs)) return;
|
|
8417
|
+
let entries;
|
|
8418
|
+
try {
|
|
8419
|
+
entries = readdirSync4(kindAbs);
|
|
8420
|
+
} catch {
|
|
8421
|
+
return;
|
|
8422
|
+
}
|
|
8423
|
+
entries.sort();
|
|
8424
|
+
for (const entry of entries) {
|
|
8425
|
+
if (entry.startsWith(".")) continue;
|
|
8426
|
+
const entryAbs = resolve16(kindAbs, entry);
|
|
8427
|
+
if (!isDirectorySafe2(entryAbs)) continue;
|
|
8428
|
+
const candidate = findIndexCandidate(entryAbs);
|
|
8429
|
+
if (candidate !== null) {
|
|
8430
|
+
out.push(`${kindDir}/${entry}/${candidate}`);
|
|
8431
|
+
}
|
|
8432
|
+
}
|
|
8433
|
+
}
|
|
8434
|
+
function isDirectorySafe2(path) {
|
|
8435
|
+
try {
|
|
8436
|
+
return statSync2(path).isDirectory();
|
|
8437
|
+
} catch {
|
|
8438
|
+
return false;
|
|
8439
|
+
}
|
|
8440
|
+
}
|
|
8441
|
+
function findIndexCandidate(entryAbs) {
|
|
8442
|
+
for (const candidate of INDEX_CANDIDATES) {
|
|
8443
|
+
if (existsSync12(resolve16(entryAbs, candidate))) return candidate;
|
|
8444
|
+
}
|
|
8445
|
+
return null;
|
|
8446
|
+
}
|
|
8284
8447
|
function installedSpecVersion() {
|
|
8285
8448
|
const require2 = createRequire5(import.meta.url);
|
|
8286
8449
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
8287
8450
|
const pkgPath = resolve16(indexPath, "..", "package.json");
|
|
8288
|
-
const pkg = JSON.parse(
|
|
8451
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
8289
8452
|
return pkg.version;
|
|
8290
8453
|
}
|
|
8291
8454
|
|
|
@@ -8382,11 +8545,11 @@ async function buildEnabledResolver(ctx) {
|
|
|
8382
8545
|
|
|
8383
8546
|
// kernel/scan/walk-content.ts
|
|
8384
8547
|
import { readFile, readdir, lstat } from "fs/promises";
|
|
8385
|
-
import { join as
|
|
8548
|
+
import { join as join9, relative as relative2, sep as sep2 } from "path";
|
|
8386
8549
|
|
|
8387
8550
|
// kernel/scan/ignore.ts
|
|
8388
|
-
import { existsSync as
|
|
8389
|
-
import { dirname as
|
|
8551
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
|
|
8552
|
+
import { dirname as dirname10, resolve as resolve17 } from "path";
|
|
8390
8553
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8391
8554
|
import ignoreFactory from "ignore";
|
|
8392
8555
|
function buildIgnoreFilter(opts = {}) {
|
|
@@ -8416,9 +8579,9 @@ function loadBundledIgnoreText() {
|
|
|
8416
8579
|
}
|
|
8417
8580
|
function readIgnoreFileText(scopeRoot) {
|
|
8418
8581
|
const path = resolve17(scopeRoot, ".skillmapignore");
|
|
8419
|
-
if (!
|
|
8582
|
+
if (!existsSync13(path)) return void 0;
|
|
8420
8583
|
try {
|
|
8421
|
-
return
|
|
8584
|
+
return readFileSync13(path, "utf8");
|
|
8422
8585
|
} catch {
|
|
8423
8586
|
return void 0;
|
|
8424
8587
|
}
|
|
@@ -8442,7 +8605,7 @@ function loadDefaultsText() {
|
|
|
8442
8605
|
return cachedDefaults;
|
|
8443
8606
|
}
|
|
8444
8607
|
function readDefaultsFromDisk() {
|
|
8445
|
-
const here =
|
|
8608
|
+
const here = dirname10(fileURLToPath2(import.meta.url));
|
|
8446
8609
|
const candidates = [
|
|
8447
8610
|
resolve17(here, "../../config/defaults/skillmapignore"),
|
|
8448
8611
|
// src/kernel/scan/ → src/config/defaults/
|
|
@@ -8451,9 +8614,9 @@ function readDefaultsFromDisk() {
|
|
|
8451
8614
|
resolve17(here, "config/defaults/skillmapignore")
|
|
8452
8615
|
];
|
|
8453
8616
|
for (const candidate of candidates) {
|
|
8454
|
-
if (
|
|
8617
|
+
if (existsSync13(candidate)) {
|
|
8455
8618
|
try {
|
|
8456
|
-
return
|
|
8619
|
+
return readFileSync13(candidate, "utf8");
|
|
8457
8620
|
} catch {
|
|
8458
8621
|
}
|
|
8459
8622
|
}
|
|
@@ -8461,7 +8624,7 @@ function readDefaultsFromDisk() {
|
|
|
8461
8624
|
return "";
|
|
8462
8625
|
}
|
|
8463
8626
|
|
|
8464
|
-
//
|
|
8627
|
+
// plugins/core/parsers/frontmatter-yaml/index.ts
|
|
8465
8628
|
import yaml3 from "js-yaml";
|
|
8466
8629
|
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
8467
8630
|
var frontmatterYamlParser = {
|
|
@@ -8496,7 +8659,7 @@ function sanitiseParseErrorMessage(err) {
|
|
|
8496
8659
|
return raw.replace(/[-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
8497
8660
|
}
|
|
8498
8661
|
|
|
8499
|
-
//
|
|
8662
|
+
// plugins/core/parsers/plain/index.ts
|
|
8500
8663
|
var plainParser = {
|
|
8501
8664
|
id: "plain",
|
|
8502
8665
|
parse(raw, _path) {
|
|
@@ -8559,7 +8722,7 @@ async function* walkRoot(root, current, filter, extensions) {
|
|
|
8559
8722
|
}
|
|
8560
8723
|
for (const entry of entries) {
|
|
8561
8724
|
const name = entry.name;
|
|
8562
|
-
const full =
|
|
8725
|
+
const full = join9(current, name);
|
|
8563
8726
|
const rel = relative2(root, full).split(sep2).join("/");
|
|
8564
8727
|
if (filter.ignores(rel)) continue;
|
|
8565
8728
|
if (entry.isSymbolicLink()) continue;
|
|
@@ -8605,7 +8768,7 @@ function resolveProviderWalk(provider) {
|
|
|
8605
8768
|
// kernel/extensions/collect-view-contributions.ts
|
|
8606
8769
|
function collectViewContributions(pluginId, extensionId, instance, out, options = {}) {
|
|
8607
8770
|
if (typeof instance !== "object" || instance === null) return;
|
|
8608
|
-
const raw = instance["
|
|
8771
|
+
const raw = instance["ui"];
|
|
8609
8772
|
if (typeof raw !== "object" || raw === null) return;
|
|
8610
8773
|
const exclude = options.excludeQualifiedIds;
|
|
8611
8774
|
for (const [contributionId, value] of Object.entries(raw)) {
|
|
@@ -8649,6 +8812,7 @@ function bucketLoaded(loaded, bundle) {
|
|
|
8649
8812
|
pluginId: ext.pluginId,
|
|
8650
8813
|
kind: ext.kind,
|
|
8651
8814
|
version: ext.version,
|
|
8815
|
+
description: instance.description ?? "",
|
|
8652
8816
|
...ext.entryPath ? { entry: ext.entryPath } : {}
|
|
8653
8817
|
});
|
|
8654
8818
|
collectAnnotationContributions(ext.pluginId, instance, bundle.annotationContributions);
|
|
@@ -8656,21 +8820,25 @@ function bucketLoaded(loaded, bundle) {
|
|
|
8656
8820
|
}
|
|
8657
8821
|
}
|
|
8658
8822
|
function collectAnnotationContributions(pluginId, instance, out) {
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8823
|
+
const row = tryReadAnnotationRow(pluginId, instance);
|
|
8824
|
+
if (row !== null) out.push(row);
|
|
8825
|
+
}
|
|
8826
|
+
function tryReadAnnotationRow(pluginId, instance) {
|
|
8827
|
+
if (typeof instance !== "object" || instance === null) return null;
|
|
8828
|
+
const inst = instance;
|
|
8829
|
+
const raw = inst["annotation"];
|
|
8830
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
8831
|
+
const entry = raw;
|
|
8832
|
+
if (typeof entry.schema !== "object" || entry.schema === null) return null;
|
|
8833
|
+
const extId = inst["id"];
|
|
8834
|
+
if (typeof extId !== "string" || extId.length === 0) return null;
|
|
8835
|
+
return {
|
|
8836
|
+
pluginId,
|
|
8837
|
+
key: extId,
|
|
8838
|
+
location: entry.location ?? "namespaced",
|
|
8839
|
+
ownership: entry.ownership ?? "shared",
|
|
8840
|
+
schema: entry.schema
|
|
8841
|
+
};
|
|
8674
8842
|
}
|
|
8675
8843
|
function isExtensionInstance(v) {
|
|
8676
8844
|
return typeof v === "object" && v !== null && typeof v["id"] === "string" && typeof v["kind"] === "string" && typeof v["version"] === "string";
|
|
@@ -8821,7 +8989,7 @@ function collectRegisteredContributionKeys(composed) {
|
|
|
8821
8989
|
const keys = /* @__PURE__ */ new Set();
|
|
8822
8990
|
if (!composed) return keys;
|
|
8823
8991
|
for (const ext of [...composed.extractors, ...composed.analyzers]) {
|
|
8824
|
-
const raw = ext.
|
|
8992
|
+
const raw = ext.ui;
|
|
8825
8993
|
if (typeof raw !== "object" || raw === null) continue;
|
|
8826
8994
|
for (const [contributionId, value] of Object.entries(raw)) {
|
|
8827
8995
|
if (typeof value !== "object" || value === null) continue;
|
|
@@ -9162,7 +9330,7 @@ function trimRedundantPath(message, primary) {
|
|
|
9162
9330
|
}
|
|
9163
9331
|
|
|
9164
9332
|
// cli/commands/config.ts
|
|
9165
|
-
import { existsSync as
|
|
9333
|
+
import { existsSync as existsSync14 } from "fs";
|
|
9166
9334
|
import { Command as Command4, Option as Option4 } from "clipanion";
|
|
9167
9335
|
|
|
9168
9336
|
// cli/util/path-display.ts
|
|
@@ -9690,7 +9858,7 @@ var ConfigResetCommand = class extends SmCommand {
|
|
|
9690
9858
|
const path = targetSettingsPath2(target, ctx.cwd);
|
|
9691
9859
|
const ansi = this.ansiFor("stdout");
|
|
9692
9860
|
const okGlyph = ansi.green("\u2713");
|
|
9693
|
-
if (!
|
|
9861
|
+
if (!existsSync14(path)) {
|
|
9694
9862
|
this.printer.data(
|
|
9695
9863
|
tx(CONFIG_TEXTS.unsetNoOverride, {
|
|
9696
9864
|
glyph: okGlyph,
|
|
@@ -9765,16 +9933,16 @@ var CONFIG_COMMANDS = [
|
|
|
9765
9933
|
];
|
|
9766
9934
|
|
|
9767
9935
|
// cli/commands/conformance.ts
|
|
9768
|
-
import { existsSync as
|
|
9769
|
-
import { dirname as
|
|
9936
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
9937
|
+
import { dirname as dirname12, resolve as resolve21 } from "path";
|
|
9770
9938
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
9771
9939
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
9772
9940
|
|
|
9773
9941
|
// conformance/index.ts
|
|
9774
9942
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9775
|
-
import { cpSync, existsSync as
|
|
9943
|
+
import { cpSync, existsSync as existsSync15, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync14, rmSync, statSync as statSync3 } from "fs";
|
|
9776
9944
|
import { tmpdir } from "os";
|
|
9777
|
-
import { isAbsolute as isAbsolute5, join as
|
|
9945
|
+
import { isAbsolute as isAbsolute5, join as join10, relative as relative3, resolve as resolve19 } from "path";
|
|
9778
9946
|
|
|
9779
9947
|
// conformance/i18n/runner.texts.ts
|
|
9780
9948
|
var CONFORMANCE_RUNNER_TEXTS = {
|
|
@@ -9808,11 +9976,11 @@ function disableEnv(setup) {
|
|
|
9808
9976
|
return env;
|
|
9809
9977
|
}
|
|
9810
9978
|
function runConformanceCase(options) {
|
|
9811
|
-
const raw =
|
|
9979
|
+
const raw = readFileSync14(options.casePath, "utf8");
|
|
9812
9980
|
const c = JSON.parse(raw);
|
|
9813
|
-
const fixturesRoot = options.fixturesRoot ??
|
|
9981
|
+
const fixturesRoot = options.fixturesRoot ?? join10(options.specRoot, "conformance", "fixtures");
|
|
9814
9982
|
const safeId = c.id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 32);
|
|
9815
|
-
const scope = mkdtempSync(
|
|
9983
|
+
const scope = mkdtempSync(join10(tmpdir(), `sm-conformance-${safeId}-`));
|
|
9816
9984
|
const setupEnv = disableEnv(c.setup);
|
|
9817
9985
|
try {
|
|
9818
9986
|
const priorFailure = runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv);
|
|
@@ -9882,11 +10050,11 @@ function runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv) {
|
|
|
9882
10050
|
}
|
|
9883
10051
|
function replaceFixture(scope, fixturesRoot, fixture) {
|
|
9884
10052
|
assertContained2(fixturesRoot, fixture, "fixture");
|
|
9885
|
-
for (const entry of
|
|
10053
|
+
for (const entry of readdirSync5(scope)) {
|
|
9886
10054
|
if (entry === KERNEL_SKILL_MAP_DIR) continue;
|
|
9887
|
-
rmSync(
|
|
10055
|
+
rmSync(join10(scope, entry), { recursive: true, force: true });
|
|
9888
10056
|
}
|
|
9889
|
-
const src =
|
|
10057
|
+
const src = join10(fixturesRoot, fixture);
|
|
9890
10058
|
cpSync(src, scope, { recursive: true });
|
|
9891
10059
|
}
|
|
9892
10060
|
function assertContained2(root, rel, label) {
|
|
@@ -9923,7 +10091,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
9923
10091
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
9924
10092
|
}
|
|
9925
10093
|
const abs = resolve19(ctx.scope, a.path);
|
|
9926
|
-
return
|
|
10094
|
+
return existsSync15(abs) ? { ok: true, type: a.type } : {
|
|
9927
10095
|
ok: false,
|
|
9928
10096
|
type: a.type,
|
|
9929
10097
|
reason: tx(CONFORMANCE_RUNNER_TEXTS.fileNotFound, { path: a.path })
|
|
@@ -9936,17 +10104,17 @@ function evaluateAssertion(a, ctx) {
|
|
|
9936
10104
|
} catch (err) {
|
|
9937
10105
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
9938
10106
|
}
|
|
9939
|
-
const fixturePath =
|
|
10107
|
+
const fixturePath = join10(ctx.fixturesRoot, a.fixture);
|
|
9940
10108
|
const targetPath = resolve19(ctx.scope, a.path);
|
|
9941
|
-
if (!
|
|
10109
|
+
if (!existsSync15(targetPath)) {
|
|
9942
10110
|
return {
|
|
9943
10111
|
ok: false,
|
|
9944
10112
|
type: a.type,
|
|
9945
10113
|
reason: tx(CONFORMANCE_RUNNER_TEXTS.targetNotFound, { path: a.path })
|
|
9946
10114
|
};
|
|
9947
10115
|
}
|
|
9948
|
-
const needle =
|
|
9949
|
-
const haystack =
|
|
10116
|
+
const needle = readFileSync14(fixturePath);
|
|
10117
|
+
const haystack = readFileSync14(targetPath);
|
|
9950
10118
|
return haystack.includes(needle) ? { ok: true, type: a.type } : {
|
|
9951
10119
|
ok: false,
|
|
9952
10120
|
type: a.type,
|
|
@@ -10119,15 +10287,15 @@ var CONFORMANCE_TEXTS = {
|
|
|
10119
10287
|
};
|
|
10120
10288
|
|
|
10121
10289
|
// cli/util/conformance-scopes.ts
|
|
10122
|
-
import { existsSync as
|
|
10123
|
-
import { dirname as
|
|
10290
|
+
import { existsSync as existsSync16, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
10291
|
+
import { dirname as dirname11, resolve as resolve20 } from "path";
|
|
10124
10292
|
import { createRequire as createRequire6 } from "module";
|
|
10125
10293
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10126
10294
|
function resolveSpecRoot4() {
|
|
10127
10295
|
const require2 = createRequire6(import.meta.url);
|
|
10128
10296
|
try {
|
|
10129
10297
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
10130
|
-
return
|
|
10298
|
+
return dirname11(indexPath);
|
|
10131
10299
|
} catch {
|
|
10132
10300
|
throw new Error(
|
|
10133
10301
|
"@skill-map/spec not resolvable: ensure the workspace is linked or the package is installed."
|
|
@@ -10135,19 +10303,19 @@ function resolveSpecRoot4() {
|
|
|
10135
10303
|
}
|
|
10136
10304
|
}
|
|
10137
10305
|
function resolveCliWorkspaceRoot() {
|
|
10138
|
-
const here =
|
|
10306
|
+
const here = dirname11(fileURLToPath3(import.meta.url));
|
|
10139
10307
|
let cursor = here;
|
|
10140
10308
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
10141
|
-
const candidate = resolve20(cursor, "
|
|
10142
|
-
if (
|
|
10309
|
+
const candidate = resolve20(cursor, "plugins");
|
|
10310
|
+
if (existsSync16(candidate) && statSync4(candidate).isDirectory()) {
|
|
10143
10311
|
return cursor;
|
|
10144
10312
|
}
|
|
10145
|
-
const parent =
|
|
10313
|
+
const parent = dirname11(cursor);
|
|
10146
10314
|
if (parent === cursor) break;
|
|
10147
10315
|
cursor = parent;
|
|
10148
10316
|
}
|
|
10149
10317
|
throw new Error(
|
|
10150
|
-
`sm conformance: built-in Provider conformance assets not found (expected a '
|
|
10318
|
+
`sm conformance: built-in Provider conformance assets not found (expected a 'plugins/' directory above ${here}). The bundled CLI may not yet copy the assets; run from the source workspace, or rebuild after enabling the asset-copy step.`
|
|
10151
10319
|
);
|
|
10152
10320
|
}
|
|
10153
10321
|
function collectProviderScopes(specRoot) {
|
|
@@ -10158,16 +10326,33 @@ function collectProviderScopes(specRoot) {
|
|
|
10158
10326
|
} catch {
|
|
10159
10327
|
return out;
|
|
10160
10328
|
}
|
|
10161
|
-
const
|
|
10162
|
-
if (!
|
|
10163
|
-
for (const
|
|
10329
|
+
const pluginsRoot = resolve20(workspaceRoot, "plugins");
|
|
10330
|
+
if (!existsSync16(pluginsRoot)) return out;
|
|
10331
|
+
for (const bundleEntry of readdirSync6(pluginsRoot)) {
|
|
10332
|
+
const bundleDir = resolve20(pluginsRoot, bundleEntry);
|
|
10333
|
+
if (!isDir(bundleDir)) continue;
|
|
10334
|
+
const providersRoot = resolve20(bundleDir, "providers");
|
|
10335
|
+
if (!isDir(providersRoot)) continue;
|
|
10336
|
+
collectBundleProviderScopes(providersRoot, specRoot, out);
|
|
10337
|
+
}
|
|
10338
|
+
return out;
|
|
10339
|
+
}
|
|
10340
|
+
function isDir(path) {
|
|
10341
|
+
try {
|
|
10342
|
+
return existsSync16(path) && statSync4(path).isDirectory();
|
|
10343
|
+
} catch {
|
|
10344
|
+
return false;
|
|
10345
|
+
}
|
|
10346
|
+
}
|
|
10347
|
+
function collectBundleProviderScopes(providersRoot, specRoot, out) {
|
|
10348
|
+
for (const entry of readdirSync6(providersRoot)) {
|
|
10164
10349
|
const providerDir = resolve20(providersRoot, entry);
|
|
10165
|
-
if (!
|
|
10350
|
+
if (!isDir(providerDir)) continue;
|
|
10166
10351
|
const conformanceDir = resolve20(providerDir, "conformance");
|
|
10167
|
-
if (!
|
|
10352
|
+
if (!existsSync16(conformanceDir)) continue;
|
|
10168
10353
|
const casesDir = resolve20(conformanceDir, "cases");
|
|
10169
10354
|
const fixturesDir = resolve20(conformanceDir, "fixtures");
|
|
10170
|
-
if (!
|
|
10355
|
+
if (!existsSync16(casesDir) || !existsSync16(fixturesDir)) continue;
|
|
10171
10356
|
out.push({
|
|
10172
10357
|
id: `provider:${entry}`,
|
|
10173
10358
|
kind: "provider",
|
|
@@ -10177,7 +10362,6 @@ function collectProviderScopes(specRoot) {
|
|
|
10177
10362
|
specRoot
|
|
10178
10363
|
});
|
|
10179
10364
|
}
|
|
10180
|
-
return out;
|
|
10181
10365
|
}
|
|
10182
10366
|
function specScope(specRoot) {
|
|
10183
10367
|
return {
|
|
@@ -10206,8 +10390,8 @@ function selectConformanceScopes(scope) {
|
|
|
10206
10390
|
return [match];
|
|
10207
10391
|
}
|
|
10208
10392
|
function listCaseFiles(scope) {
|
|
10209
|
-
if (!
|
|
10210
|
-
return
|
|
10393
|
+
if (!existsSync16(scope.casesDir)) return [];
|
|
10394
|
+
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve20(scope.casesDir, entry));
|
|
10211
10395
|
}
|
|
10212
10396
|
|
|
10213
10397
|
// cli/commands/conformance.ts
|
|
@@ -10221,12 +10405,12 @@ function formatAssertionFailureDetail(type, reason) {
|
|
|
10221
10405
|
});
|
|
10222
10406
|
}
|
|
10223
10407
|
function resolveBinary() {
|
|
10224
|
-
const here =
|
|
10408
|
+
const here = dirname12(fileURLToPath4(import.meta.url));
|
|
10225
10409
|
let cursor = here;
|
|
10226
10410
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
10227
10411
|
const candidate = resolve21(cursor, "bin", "sm.js");
|
|
10228
|
-
if (
|
|
10229
|
-
const parent =
|
|
10412
|
+
if (existsSync17(candidate)) return candidate;
|
|
10413
|
+
const parent = dirname12(cursor);
|
|
10230
10414
|
if (parent === cursor) break;
|
|
10231
10415
|
cursor = parent;
|
|
10232
10416
|
}
|
|
@@ -10291,7 +10475,7 @@ var ConformanceRunCommand = class extends SmCommand {
|
|
|
10291
10475
|
return ExitCode.Error;
|
|
10292
10476
|
}
|
|
10293
10477
|
const binary = resolveBinary();
|
|
10294
|
-
if (!
|
|
10478
|
+
if (!existsSync17(binary)) {
|
|
10295
10479
|
if (this.json) {
|
|
10296
10480
|
this.#emitJsonError(
|
|
10297
10481
|
"internal",
|
|
@@ -10465,7 +10649,7 @@ function projectAssertionFailures(assertions) {
|
|
|
10465
10649
|
}
|
|
10466
10650
|
function readCaseId(casePath) {
|
|
10467
10651
|
try {
|
|
10468
|
-
const raw =
|
|
10652
|
+
const raw = readFileSync15(casePath, "utf8");
|
|
10469
10653
|
const parsed = JSON.parse(raw);
|
|
10470
10654
|
if (typeof parsed.id === "string") return parsed.id;
|
|
10471
10655
|
} catch {
|
|
@@ -10483,7 +10667,7 @@ function writeStreamSnippet(stream, header, text) {
|
|
|
10483
10667
|
var CONFORMANCE_COMMANDS = [ConformanceRunCommand];
|
|
10484
10668
|
|
|
10485
10669
|
// cli/commands/db/backup.ts
|
|
10486
|
-
import { dirname as
|
|
10670
|
+
import { dirname as dirname13, join as join11, resolve as resolve22 } from "path";
|
|
10487
10671
|
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
10488
10672
|
|
|
10489
10673
|
// cli/i18n/db.texts.ts
|
|
@@ -10569,7 +10753,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
10569
10753
|
const exit = requireDbOrExit(path, this.context.stderr);
|
|
10570
10754
|
if (exit !== null) return exit;
|
|
10571
10755
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
10572
|
-
const outPath = this.out ? resolve22(this.out) :
|
|
10756
|
+
const outPath = this.out ? resolve22(this.out) : join11(dirname13(path), "backups", `${ts}.db`);
|
|
10573
10757
|
await withSqlite({ databasePath: path, autoMigrate: false }, async (storage) => {
|
|
10574
10758
|
storage.migrations.writeBackup(outPath);
|
|
10575
10759
|
});
|
|
@@ -10586,7 +10770,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
10586
10770
|
|
|
10587
10771
|
// cli/commands/db/restore.ts
|
|
10588
10772
|
import { chmod, copyFile, mkdir, rm } from "fs/promises";
|
|
10589
|
-
import { dirname as
|
|
10773
|
+
import { dirname as dirname14, resolve as resolve23 } from "path";
|
|
10590
10774
|
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
10591
10775
|
|
|
10592
10776
|
// cli/util/fs.ts
|
|
@@ -10669,7 +10853,7 @@ var DbRestoreCommand = class extends SmCommand {
|
|
|
10669
10853
|
return ExitCode.Error;
|
|
10670
10854
|
}
|
|
10671
10855
|
}
|
|
10672
|
-
await mkdir(
|
|
10856
|
+
await mkdir(dirname14(target), { recursive: true });
|
|
10673
10857
|
await copyFile(sourcePath, target);
|
|
10674
10858
|
await chmodOwnerOnlyBestEffort(target);
|
|
10675
10859
|
for (const sidecar of [`${target}-wal`, `${target}-shm`]) {
|
|
@@ -11018,7 +11202,7 @@ function formatSqlValue(value) {
|
|
|
11018
11202
|
|
|
11019
11203
|
// cli/commands/db/migrate.ts
|
|
11020
11204
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
11021
|
-
import { dirname as
|
|
11205
|
+
import { dirname as dirname15 } from "path";
|
|
11022
11206
|
import { Command as Command12, Option as Option11 } from "clipanion";
|
|
11023
11207
|
|
|
11024
11208
|
// cli/i18n/option-validators.texts.ts
|
|
@@ -11105,7 +11289,7 @@ var DbMigrateCommand = class extends SmCommand {
|
|
|
11105
11289
|
return ExitCode.Error;
|
|
11106
11290
|
}
|
|
11107
11291
|
const path = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
11108
|
-
if (path !== ":memory:") await mkdir2(
|
|
11292
|
+
if (path !== ":memory:") await mkdir2(dirname15(path), { recursive: true });
|
|
11109
11293
|
const adapter = createSqliteStorage({
|
|
11110
11294
|
databasePath: path,
|
|
11111
11295
|
autoMigrate: false
|
|
@@ -11758,7 +11942,7 @@ var GraphCommand = class extends SmCommand {
|
|
|
11758
11942
|
};
|
|
11759
11943
|
|
|
11760
11944
|
// cli/commands/help.ts
|
|
11761
|
-
import { readFileSync as
|
|
11945
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
11762
11946
|
import { createRequire as createRequire7 } from "module";
|
|
11763
11947
|
import { resolve as resolve25 } from "path";
|
|
11764
11948
|
import { Command as Command15, Option as Option14 } from "clipanion";
|
|
@@ -11981,7 +12165,7 @@ function resolveSpecVersion() {
|
|
|
11981
12165
|
const req = createRequire7(import.meta.url);
|
|
11982
12166
|
const indexPath = req.resolve("@skill-map/spec/index.json");
|
|
11983
12167
|
const pkgPath = resolve25(indexPath, "..", "package.json");
|
|
11984
|
-
const pkg = JSON.parse(
|
|
12168
|
+
const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
|
|
11985
12169
|
return pkg.version;
|
|
11986
12170
|
} catch {
|
|
11987
12171
|
return "unknown";
|
|
@@ -12233,13 +12417,13 @@ function registeredVerbPaths(cli2) {
|
|
|
12233
12417
|
// cli/commands/hooks.ts
|
|
12234
12418
|
import {
|
|
12235
12419
|
chmodSync,
|
|
12236
|
-
existsSync as
|
|
12420
|
+
existsSync as existsSync18,
|
|
12237
12421
|
mkdirSync as mkdirSync5,
|
|
12238
|
-
readFileSync as
|
|
12239
|
-
statSync as
|
|
12422
|
+
readFileSync as readFileSync17,
|
|
12423
|
+
statSync as statSync5,
|
|
12240
12424
|
writeFileSync as writeFileSync2
|
|
12241
12425
|
} from "fs";
|
|
12242
|
-
import { dirname as
|
|
12426
|
+
import { dirname as dirname16, resolve as resolve26 } from "path";
|
|
12243
12427
|
import { Command as Command16, Option as Option15 } from "clipanion";
|
|
12244
12428
|
|
|
12245
12429
|
// cli/i18n/hooks.texts.ts
|
|
@@ -12332,7 +12516,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12332
12516
|
}
|
|
12333
12517
|
const hooksDir = resolve26(repoRoot, ".git", "hooks");
|
|
12334
12518
|
const hookPath = resolve26(hooksDir, "pre-commit");
|
|
12335
|
-
const existing =
|
|
12519
|
+
const existing = existsSync18(hookPath) ? readFileSync17(hookPath, "utf8") : null;
|
|
12336
12520
|
const planned2 = computePlannedHookContent(existing);
|
|
12337
12521
|
if (planned2.kind === "already-installed") {
|
|
12338
12522
|
this.printer.info(tx(HOOKS_TEXTS.alreadyInstalled, { glyph: okGlyph, hookPath }));
|
|
@@ -12358,7 +12542,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12358
12542
|
return ExitCode.Ok;
|
|
12359
12543
|
}
|
|
12360
12544
|
try {
|
|
12361
|
-
if (!
|
|
12545
|
+
if (!existsSync18(hooksDir)) mkdirSync5(hooksDir, { recursive: true });
|
|
12362
12546
|
writeFileSync2(hookPath, planned2.content, { encoding: "utf8" });
|
|
12363
12547
|
ensureExecutableBit(hookPath);
|
|
12364
12548
|
} catch (err) {
|
|
@@ -12389,8 +12573,8 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12389
12573
|
function findGitRepoRoot(cwd) {
|
|
12390
12574
|
let current = cwd;
|
|
12391
12575
|
while (true) {
|
|
12392
|
-
if (
|
|
12393
|
-
const parent =
|
|
12576
|
+
if (existsSync18(resolve26(current, ".git"))) return current;
|
|
12577
|
+
const parent = dirname16(current);
|
|
12394
12578
|
if (parent === current) return null;
|
|
12395
12579
|
current = parent;
|
|
12396
12580
|
}
|
|
@@ -12404,18 +12588,18 @@ function computePlannedHookContent(existing) {
|
|
|
12404
12588
|
return { kind: "chained", content: existing + sep6 + "\n" + SKILL_MAP_BLOCK };
|
|
12405
12589
|
}
|
|
12406
12590
|
function ensureExecutableBit(path) {
|
|
12407
|
-
const mode =
|
|
12591
|
+
const mode = statSync5(path).mode;
|
|
12408
12592
|
chmodSync(path, mode | 73);
|
|
12409
12593
|
}
|
|
12410
12594
|
var HOOKS_COMMANDS = [HooksInstallCommand];
|
|
12411
12595
|
|
|
12412
12596
|
// cli/commands/init.ts
|
|
12413
12597
|
import { mkdir as mkdir3, readFile as readFile2, writeFile } from "fs/promises";
|
|
12414
|
-
import { join as
|
|
12598
|
+
import { join as join15 } from "path";
|
|
12415
12599
|
import { Command as Command17, Option as Option16 } from "clipanion";
|
|
12416
12600
|
|
|
12417
12601
|
// kernel/orchestrator/index.ts
|
|
12418
|
-
import { existsSync as
|
|
12602
|
+
import { existsSync as existsSync21, statSync as statSync7 } from "fs";
|
|
12419
12603
|
import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
12420
12604
|
import cl100k_base from "js-tiktoken/ranks/cl100k_base";
|
|
12421
12605
|
|
|
@@ -12533,7 +12717,7 @@ async function runExtractorsForNode(opts) {
|
|
|
12533
12717
|
}
|
|
12534
12718
|
function readDeclaredContributions(extension) {
|
|
12535
12719
|
const out = /* @__PURE__ */ new Map();
|
|
12536
|
-
const raw = extension.
|
|
12720
|
+
const raw = extension.ui;
|
|
12537
12721
|
if (typeof raw !== "object" || raw === null) return out;
|
|
12538
12722
|
for (const [id, value] of Object.entries(raw)) {
|
|
12539
12723
|
if (typeof value !== "object" || value === null) continue;
|
|
@@ -12554,11 +12738,13 @@ function emitExtensionError(emitter, qualifiedId2, nodePath, data) {
|
|
|
12554
12738
|
);
|
|
12555
12739
|
}
|
|
12556
12740
|
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, emitContribution, store) {
|
|
12557
|
-
const scope = extractor.scope;
|
|
12741
|
+
const scope = extractor.scope ?? "both";
|
|
12742
|
+
const settings = extractor.resolvedSettings ?? {};
|
|
12558
12743
|
return {
|
|
12559
12744
|
node,
|
|
12560
12745
|
body: scope === "frontmatter" ? "" : body,
|
|
12561
12746
|
frontmatter: scope === "body" ? {} : frontmatter,
|
|
12747
|
+
settings,
|
|
12562
12748
|
emitLink,
|
|
12563
12749
|
enrichNode,
|
|
12564
12750
|
emitContribution,
|
|
@@ -12566,25 +12752,26 @@ function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enr
|
|
|
12566
12752
|
};
|
|
12567
12753
|
}
|
|
12568
12754
|
function validateLink(extractor, link2, emitter) {
|
|
12569
|
-
|
|
12755
|
+
const knownKinds = ["invokes", "references", "mentions", "supersedes"];
|
|
12756
|
+
if (!knownKinds.includes(link2.kind)) {
|
|
12570
12757
|
const qualifiedId2 = `${extractor.pluginId}/${extractor.id}`;
|
|
12571
12758
|
emitter.emit(
|
|
12572
12759
|
makeEvent("extension.error", {
|
|
12573
12760
|
kind: "link-kind-not-declared",
|
|
12574
12761
|
extensionId: qualifiedId2,
|
|
12575
12762
|
linkKind: link2.kind,
|
|
12576
|
-
declaredKinds:
|
|
12763
|
+
declaredKinds: knownKinds,
|
|
12577
12764
|
link: { source: link2.source, target: link2.target, kind: link2.kind },
|
|
12578
12765
|
message: tx(ORCHESTRATOR_TEXTS.extensionErrorLinkKindNotDeclared, {
|
|
12579
12766
|
extractorId: qualifiedId2,
|
|
12580
12767
|
linkKind: link2.kind,
|
|
12581
|
-
declaredKinds:
|
|
12768
|
+
declaredKinds: knownKinds.join(", ")
|
|
12582
12769
|
})
|
|
12583
12770
|
})
|
|
12584
12771
|
);
|
|
12585
12772
|
return null;
|
|
12586
12773
|
}
|
|
12587
|
-
const confidence = link2.confidence ??
|
|
12774
|
+
const confidence = link2.confidence ?? "medium";
|
|
12588
12775
|
return { ...link2, confidence };
|
|
12589
12776
|
}
|
|
12590
12777
|
function dedupeLinks(links) {
|
|
@@ -12642,7 +12829,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
12642
12829
|
const issues = [];
|
|
12643
12830
|
const contributions = [];
|
|
12644
12831
|
const validators = loadSchemaValidators();
|
|
12645
|
-
|
|
12832
|
+
void registeredActionIds;
|
|
12646
12833
|
const analyzerOrphans = orphanSidecars.map((o) => ({
|
|
12647
12834
|
relativePath: o.relativePath,
|
|
12648
12835
|
expectedMdPath: o.expectedMdPath
|
|
@@ -12714,27 +12901,6 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
12714
12901
|
}
|
|
12715
12902
|
return { issues, contributions };
|
|
12716
12903
|
}
|
|
12717
|
-
function validateRecommendedActions(analyzers, registeredActionIds, emitter) {
|
|
12718
|
-
for (const analyzer of analyzers) {
|
|
12719
|
-
const refs = analyzer.recommendedActions;
|
|
12720
|
-
if (refs === void 0 || refs.length === 0) continue;
|
|
12721
|
-
const analyzerId = qualifiedExtensionId(analyzer.pluginId, analyzer.id);
|
|
12722
|
-
for (const actionId of refs) {
|
|
12723
|
-
if (registeredActionIds.has(actionId)) continue;
|
|
12724
|
-
emitter.emit(
|
|
12725
|
-
makeEvent("extension.error", {
|
|
12726
|
-
kind: "recommended-action-missing",
|
|
12727
|
-
extensionId: analyzerId,
|
|
12728
|
-
actionId,
|
|
12729
|
-
message: tx(ORCHESTRATOR_TEXTS.extensionErrorRecommendedActionMissing, {
|
|
12730
|
-
analyzerId,
|
|
12731
|
-
actionId
|
|
12732
|
-
})
|
|
12733
|
-
})
|
|
12734
|
-
);
|
|
12735
|
-
}
|
|
12736
|
-
}
|
|
12737
|
-
}
|
|
12738
12904
|
function validateIssue(analyzer, issue, emitter) {
|
|
12739
12905
|
const severity = issue.severity;
|
|
12740
12906
|
if (severity !== "error" && severity !== "warn" && severity !== "info") {
|
|
@@ -12812,9 +12978,15 @@ function originatingNodeOf(link2, priorNodePaths) {
|
|
|
12812
12978
|
return link2.source;
|
|
12813
12979
|
}
|
|
12814
12980
|
function computeCacheDecision(opts) {
|
|
12815
|
-
const applicableExtractors = opts.extractors.filter(
|
|
12816
|
-
|
|
12817
|
-
|
|
12981
|
+
const applicableExtractors = opts.extractors.filter((ex) => {
|
|
12982
|
+
const kinds = ex.precondition?.kind;
|
|
12983
|
+
if (!kinds || kinds.length === 0) return true;
|
|
12984
|
+
return kinds.some((qualified) => {
|
|
12985
|
+
const slashIdx = qualified.indexOf("/");
|
|
12986
|
+
const kindOnly = slashIdx === -1 ? qualified : qualified.slice(slashIdx + 1);
|
|
12987
|
+
return kindOnly === opts.kind;
|
|
12988
|
+
});
|
|
12989
|
+
});
|
|
12818
12990
|
const applicableQualifiedIds = new Set(
|
|
12819
12991
|
applicableExtractors.map((ex) => qualifiedExtensionId(ex.pluginId, ex.id))
|
|
12820
12992
|
);
|
|
@@ -13005,6 +13177,7 @@ function flagAmbiguousRenames(opts) {
|
|
|
13005
13177
|
function flagOrphans(opts) {
|
|
13006
13178
|
for (const fromPath of opts.deletedPaths) {
|
|
13007
13179
|
if (opts.claimedDeleted.has(fromPath)) continue;
|
|
13180
|
+
if (opts.silenced?.(fromPath)) continue;
|
|
13008
13181
|
opts.issues.push({
|
|
13009
13182
|
analyzerId: "orphan",
|
|
13010
13183
|
severity: "info",
|
|
@@ -13014,7 +13187,7 @@ function flagOrphans(opts) {
|
|
|
13014
13187
|
});
|
|
13015
13188
|
}
|
|
13016
13189
|
}
|
|
13017
|
-
function detectRenamesAndOrphans(prior, current, issues) {
|
|
13190
|
+
function detectRenamesAndOrphans(prior, current, issues, silenced) {
|
|
13018
13191
|
const priorByPath = /* @__PURE__ */ new Map();
|
|
13019
13192
|
for (const n of prior.nodes) priorByPath.set(n.path, n);
|
|
13020
13193
|
const currentByPath = /* @__PURE__ */ new Map();
|
|
@@ -13048,7 +13221,12 @@ function detectRenamesAndOrphans(prior, current, issues) {
|
|
|
13048
13221
|
issues
|
|
13049
13222
|
}));
|
|
13050
13223
|
flagAmbiguousRenames({ newPaths, candidatesByNew, claimedDeleted, claimedNew, issues });
|
|
13051
|
-
flagOrphans({
|
|
13224
|
+
flagOrphans({
|
|
13225
|
+
deletedPaths,
|
|
13226
|
+
claimedDeleted,
|
|
13227
|
+
issues,
|
|
13228
|
+
...silenced ? { silenced } : {}
|
|
13229
|
+
});
|
|
13052
13230
|
return ops;
|
|
13053
13231
|
}
|
|
13054
13232
|
|
|
@@ -13063,8 +13241,8 @@ function computeDriftStatus(args2) {
|
|
|
13063
13241
|
}
|
|
13064
13242
|
|
|
13065
13243
|
// kernel/sidecar/discover-orphans.ts
|
|
13066
|
-
import { existsSync as
|
|
13067
|
-
import { join as
|
|
13244
|
+
import { existsSync as existsSync19, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
13245
|
+
import { join as join12, relative as relative4, sep as sep3 } from "path";
|
|
13068
13246
|
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
13069
13247
|
const out = [];
|
|
13070
13248
|
for (const root of roots) {
|
|
@@ -13075,12 +13253,12 @@ function discoverOrphanSidecars(roots, shouldSkip) {
|
|
|
13075
13253
|
function walk(root, current, shouldSkip, out) {
|
|
13076
13254
|
let entries;
|
|
13077
13255
|
try {
|
|
13078
|
-
entries =
|
|
13256
|
+
entries = readdirSync7(current, { withFileTypes: true, encoding: "utf8" });
|
|
13079
13257
|
} catch {
|
|
13080
13258
|
return;
|
|
13081
13259
|
}
|
|
13082
13260
|
for (const entry of entries) {
|
|
13083
|
-
const full =
|
|
13261
|
+
const full = join12(current, entry.name);
|
|
13084
13262
|
const rel = relative4(root, full).split(sep3).join("/");
|
|
13085
13263
|
if (shouldSkip(rel)) continue;
|
|
13086
13264
|
if (entry.isSymbolicLink()) continue;
|
|
@@ -13091,13 +13269,13 @@ function walk(root, current, shouldSkip, out) {
|
|
|
13091
13269
|
if (!entry.isFile()) continue;
|
|
13092
13270
|
if (!entry.name.endsWith(".sm")) continue;
|
|
13093
13271
|
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
13094
|
-
if (
|
|
13272
|
+
if (existsSync19(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
13095
13273
|
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
13096
13274
|
}
|
|
13097
13275
|
}
|
|
13098
13276
|
function safeIsFile(path) {
|
|
13099
13277
|
try {
|
|
13100
|
-
return
|
|
13278
|
+
return statSync6(path).isFile();
|
|
13101
13279
|
} catch {
|
|
13102
13280
|
return false;
|
|
13103
13281
|
}
|
|
@@ -13105,7 +13283,7 @@ function safeIsFile(path) {
|
|
|
13105
13283
|
|
|
13106
13284
|
// kernel/orchestrator/node-build.ts
|
|
13107
13285
|
import { createHash } from "crypto";
|
|
13108
|
-
import { existsSync as
|
|
13286
|
+
import { existsSync as existsSync20 } from "fs";
|
|
13109
13287
|
import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
|
|
13110
13288
|
import "js-tiktoken/lite";
|
|
13111
13289
|
import yaml4 from "js-yaml";
|
|
@@ -13269,11 +13447,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
13269
13447
|
}
|
|
13270
13448
|
function resolveAbsoluteMdPath(relativePath2, roots) {
|
|
13271
13449
|
if (isAbsolute6(relativePath2)) {
|
|
13272
|
-
return
|
|
13450
|
+
return existsSync20(relativePath2) ? relativePath2 : null;
|
|
13273
13451
|
}
|
|
13274
13452
|
for (const root of roots) {
|
|
13275
13453
|
const candidate = resolvePath(root, relativePath2);
|
|
13276
|
-
if (
|
|
13454
|
+
if (existsSync20(candidate)) return candidate;
|
|
13277
13455
|
}
|
|
13278
13456
|
return null;
|
|
13279
13457
|
}
|
|
@@ -13386,6 +13564,9 @@ function buildWalkContext(opts) {
|
|
|
13386
13564
|
async function processRawNode(raw, provider, wctx, accum, claimedPaths, nextIndex) {
|
|
13387
13565
|
const bodyHash = sha256(raw.body);
|
|
13388
13566
|
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
13567
|
+
if (Array.isArray(provider.roots) && provider.roots.length > 0) {
|
|
13568
|
+
if (!matchesAnyRoot(raw.path, provider.roots)) return false;
|
|
13569
|
+
}
|
|
13389
13570
|
const kind = provider.classify(raw.path, raw.frontmatter);
|
|
13390
13571
|
if (kind === null) {
|
|
13391
13572
|
return false;
|
|
@@ -13554,6 +13735,26 @@ function recordExtractorRuns(nodePath, ctx, accum) {
|
|
|
13554
13735
|
});
|
|
13555
13736
|
}
|
|
13556
13737
|
}
|
|
13738
|
+
function matchesAnyRoot(relPath, roots) {
|
|
13739
|
+
for (const r of roots) {
|
|
13740
|
+
if (matchesOneRoot(relPath, r)) return true;
|
|
13741
|
+
}
|
|
13742
|
+
return false;
|
|
13743
|
+
}
|
|
13744
|
+
function matchesOneRoot(relPath, pattern) {
|
|
13745
|
+
if (pattern.endsWith("/**")) return matchesDeepGlob(relPath, pattern.slice(0, -3));
|
|
13746
|
+
if (pattern.endsWith("/*")) return matchesShallowGlob(relPath, pattern.slice(0, -2));
|
|
13747
|
+
return relPath === pattern;
|
|
13748
|
+
}
|
|
13749
|
+
function matchesDeepGlob(relPath, prefix) {
|
|
13750
|
+
if (prefix.length === 0) return true;
|
|
13751
|
+
return relPath === prefix || relPath.startsWith(`${prefix}/`);
|
|
13752
|
+
}
|
|
13753
|
+
function matchesShallowGlob(relPath, prefix) {
|
|
13754
|
+
if (!relPath.startsWith(`${prefix}/`)) return false;
|
|
13755
|
+
const tail = relPath.slice(prefix.length + 1);
|
|
13756
|
+
return tail.length > 0 && !tail.includes("/");
|
|
13757
|
+
}
|
|
13557
13758
|
|
|
13558
13759
|
// kernel/orchestrator/index.ts
|
|
13559
13760
|
var SCANNED_BY = {
|
|
@@ -13622,7 +13823,8 @@ async function runScanInternal(_kernel, options) {
|
|
|
13622
13823
|
mergeAnalyzerEmissions(walked, analyzerResult, exts.analyzers);
|
|
13623
13824
|
const issues = analyzerResult.issues;
|
|
13624
13825
|
for (const issue of walked.frontmatterIssues) issues.push(issue);
|
|
13625
|
-
const
|
|
13826
|
+
const silenced = options.ignoreFilter ? (path) => options.ignoreFilter.ignores(path) : void 0;
|
|
13827
|
+
const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues, silenced) : [];
|
|
13626
13828
|
const stats = buildScanStats(walked, issues, start);
|
|
13627
13829
|
const scanCompletedEvent = makeEvent("scan.completed", { stats });
|
|
13628
13830
|
emitter.emit(scanCompletedEvent);
|
|
@@ -13665,7 +13867,7 @@ async function dispatchExtractorCompleted(extractors, emitter, hookDispatcher) {
|
|
|
13665
13867
|
function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
|
|
13666
13868
|
for (const c of analyzerResult.contributions) walked.contributions.push(c);
|
|
13667
13869
|
for (const analyzer of analyzers ?? []) {
|
|
13668
|
-
if (analyzer.
|
|
13870
|
+
if (analyzer.ui === void 0) continue;
|
|
13669
13871
|
for (const node of walked.nodes) {
|
|
13670
13872
|
walked.freshlyRunTuples.add(`${analyzer.pluginId}\0${analyzer.id}\0${node.path}`);
|
|
13671
13873
|
}
|
|
@@ -13712,7 +13914,7 @@ function validateRoots(roots) {
|
|
|
13712
13914
|
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
13713
13915
|
}
|
|
13714
13916
|
for (const root of roots) {
|
|
13715
|
-
if (!
|
|
13917
|
+
if (!existsSync21(root) || !statSync7(root).isDirectory()) {
|
|
13716
13918
|
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
13717
13919
|
}
|
|
13718
13920
|
}
|
|
@@ -13940,16 +14142,16 @@ function createKernel() {
|
|
|
13940
14142
|
}
|
|
13941
14143
|
|
|
13942
14144
|
// kernel/jobs/orphan-files.ts
|
|
13943
|
-
import { readdirSync as
|
|
13944
|
-
import { join as
|
|
14145
|
+
import { readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
14146
|
+
import { join as join13, resolve as resolve28 } from "path";
|
|
13945
14147
|
function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
13946
14148
|
let entries;
|
|
13947
14149
|
try {
|
|
13948
|
-
const stat2 =
|
|
14150
|
+
const stat2 = statSync8(jobsDir);
|
|
13949
14151
|
if (!stat2.isDirectory()) {
|
|
13950
14152
|
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
13951
14153
|
}
|
|
13952
|
-
entries =
|
|
14154
|
+
entries = readdirSync8(jobsDir, { withFileTypes: true });
|
|
13953
14155
|
} catch {
|
|
13954
14156
|
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
13955
14157
|
}
|
|
@@ -13959,7 +14161,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
|
13959
14161
|
if (!entry.isFile()) continue;
|
|
13960
14162
|
const name = entry.name;
|
|
13961
14163
|
if (!name.endsWith(".md")) continue;
|
|
13962
|
-
const abs = resolve28(
|
|
14164
|
+
const abs = resolve28(join13(jobsDir, name));
|
|
13963
14165
|
if (!referencedPaths.has(abs)) orphans.push(abs);
|
|
13964
14166
|
}
|
|
13965
14167
|
orphans.sort();
|
|
@@ -14033,9 +14235,9 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
14033
14235
|
};
|
|
14034
14236
|
|
|
14035
14237
|
// core/runtime/reference-paths-walker.ts
|
|
14036
|
-
import { readdirSync as
|
|
14238
|
+
import { readdirSync as readdirSync9, statSync as statSync9 } from "fs";
|
|
14037
14239
|
import { homedir as osHomedir2 } from "os";
|
|
14038
|
-
import { isAbsolute as isAbsolute7, join as
|
|
14240
|
+
import { isAbsolute as isAbsolute7, join as join14, resolve as resolve29 } from "path";
|
|
14039
14241
|
var REFERENCE_WALK_MAX_FILES = 5e4;
|
|
14040
14242
|
var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
14041
14243
|
"node_modules",
|
|
@@ -14043,7 +14245,7 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
14043
14245
|
SKILL_MAP_DIR
|
|
14044
14246
|
]);
|
|
14045
14247
|
function resolveScanPath(raw, cwd) {
|
|
14046
|
-
if (raw.startsWith("~/")) return resolve29(
|
|
14248
|
+
if (raw.startsWith("~/")) return resolve29(join14(osHomedir2(), raw.slice(2)));
|
|
14047
14249
|
if (raw === "~") return resolve29(osHomedir2());
|
|
14048
14250
|
if (isAbsolute7(raw)) return resolve29(raw);
|
|
14049
14251
|
return resolve29(cwd, raw);
|
|
@@ -14068,14 +14270,14 @@ function walkInto(dir, out) {
|
|
|
14068
14270
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
14069
14271
|
let entries;
|
|
14070
14272
|
try {
|
|
14071
|
-
entries =
|
|
14273
|
+
entries = readdirSync9(dir, { withFileTypes: true });
|
|
14072
14274
|
} catch {
|
|
14073
14275
|
return false;
|
|
14074
14276
|
}
|
|
14075
14277
|
for (const entry of entries) {
|
|
14076
14278
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
14077
14279
|
if (entry.isSymbolicLink()) continue;
|
|
14078
|
-
const full =
|
|
14280
|
+
const full = join14(dir, entry.name);
|
|
14079
14281
|
if (entry.isDirectory()) {
|
|
14080
14282
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
14081
14283
|
if (walkInto(full, out)) return true;
|
|
@@ -14087,7 +14289,7 @@ function walkInto(dir, out) {
|
|
|
14087
14289
|
}
|
|
14088
14290
|
function safeStat(path) {
|
|
14089
14291
|
try {
|
|
14090
|
-
return
|
|
14292
|
+
return statSync9(path);
|
|
14091
14293
|
} catch {
|
|
14092
14294
|
return null;
|
|
14093
14295
|
}
|
|
@@ -14399,7 +14601,7 @@ var InitCommand = class extends SmCommand {
|
|
|
14399
14601
|
async run() {
|
|
14400
14602
|
const ctx = defaultRuntimeContext();
|
|
14401
14603
|
const scopeRoot = ctx.cwd;
|
|
14402
|
-
const skillMapDir =
|
|
14604
|
+
const skillMapDir = join15(scopeRoot, SKILL_MAP_DIR);
|
|
14403
14605
|
const settingsPath = defaultSettingsPath(scopeRoot);
|
|
14404
14606
|
const localPath = defaultLocalSettingsPath(scopeRoot);
|
|
14405
14607
|
const ignorePath = defaultIgnoreFilePath(scopeRoot);
|
|
@@ -14439,13 +14641,13 @@ var InitCommand = class extends SmCommand {
|
|
|
14439
14641
|
writeFileAtomicExclusive(localPath, "{}\n");
|
|
14440
14642
|
}
|
|
14441
14643
|
if (!await pathExists(ignorePath) || this.force) {
|
|
14442
|
-
writeFileAtomicExclusive(ignorePath, loadBundledIgnoreText());
|
|
14644
|
+
writeFileAtomicExclusive(ignorePath, loadBundledIgnoreText(), 420);
|
|
14443
14645
|
}
|
|
14444
14646
|
const ansi = this.ansiFor("stdout");
|
|
14445
14647
|
const okGlyph = ansi.green("\u2713");
|
|
14446
14648
|
const updated = await ensureGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
14447
14649
|
if (updated) {
|
|
14448
|
-
const gitignorePath =
|
|
14650
|
+
const gitignorePath = join15(scopeRoot, ".gitignore");
|
|
14449
14651
|
printer.info(
|
|
14450
14652
|
GITIGNORE_ENTRIES.length === 1 ? tx(INIT_TEXTS.gitignoreUpdatedSingular, { glyph: okGlyph, path: gitignorePath }) : tx(INIT_TEXTS.gitignoreUpdatedPlural, {
|
|
14451
14653
|
glyph: okGlyph,
|
|
@@ -14484,7 +14686,7 @@ async function dryRunFileMessage(path) {
|
|
|
14484
14686
|
}
|
|
14485
14687
|
async function writeDryRunGitignorePlan(printer, scopeRoot) {
|
|
14486
14688
|
const wouldAdd = await previewGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
14487
|
-
const gitignorePath =
|
|
14689
|
+
const gitignorePath = join15(scopeRoot, ".gitignore");
|
|
14488
14690
|
if (wouldAdd.length === 0) {
|
|
14489
14691
|
printer.info(tx(INIT_TEXTS.dryRunWouldLeaveGitignoreUnchanged, { path: gitignorePath }));
|
|
14490
14692
|
} else if (wouldAdd.length === 1) {
|
|
@@ -14559,7 +14761,7 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
14559
14761
|
return hasErrors ? ExitCode.Issues : ExitCode.Ok;
|
|
14560
14762
|
}
|
|
14561
14763
|
async function previewGitignoreEntries(scopeRoot, entries) {
|
|
14562
|
-
const path =
|
|
14764
|
+
const path = join15(scopeRoot, ".gitignore");
|
|
14563
14765
|
const body = await pathExists(path) ? await readFile2(path, "utf8") : "";
|
|
14564
14766
|
const present = new Set(
|
|
14565
14767
|
body.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"))
|
|
@@ -14567,7 +14769,7 @@ async function previewGitignoreEntries(scopeRoot, entries) {
|
|
|
14567
14769
|
return entries.filter((entry) => !present.has(entry));
|
|
14568
14770
|
}
|
|
14569
14771
|
async function ensureGitignoreEntries(scopeRoot, entries) {
|
|
14570
|
-
const path =
|
|
14772
|
+
const path = join15(scopeRoot, ".gitignore");
|
|
14571
14773
|
let body = "";
|
|
14572
14774
|
if (await pathExists(path)) {
|
|
14573
14775
|
body = await readFile2(path, "utf8");
|
|
@@ -16222,7 +16424,7 @@ var PLUGINS_TEXTS = {
|
|
|
16222
16424
|
* Success block printed after scaffolding. Follows the no-em-dash rule
|
|
16223
16425
|
* across every line.
|
|
16224
16426
|
*/
|
|
16225
|
-
createSuccess: "Created {{targetDir}}\nNext:\n - Edit {{pluginId}}/
|
|
16427
|
+
createSuccess: "Created {{targetDir}}\nNext:\n - Edit {{pluginId}}/extractors/{{pluginId}}-extractor/index.js (the extract() body)\n - Run sm scan to see the contribution surface\n - sm plugins slots list: browse other slots\n",
|
|
16226
16428
|
// --- slots list verb -------------------------------------------------
|
|
16227
16429
|
/** Section header for the view-slots catalogue. */
|
|
16228
16430
|
slotsListHeaderViewSlots: " View slots ({{count}})\n",
|
|
@@ -16282,11 +16484,9 @@ function extensionRowFromBuiltIn(ext, bundle, bundleEnabled, resolveEnabled) {
|
|
|
16282
16484
|
id: ext.id,
|
|
16283
16485
|
kind: ext.kind,
|
|
16284
16486
|
version: ext.version,
|
|
16285
|
-
enabled: bundle.granularity === "bundle" ? bundleEnabled : resolveEnabled(qualifiedExtensionId(bundle.id, ext.id))
|
|
16487
|
+
enabled: bundle.granularity === "bundle" ? bundleEnabled : resolveEnabled(qualifiedExtensionId(bundle.id, ext.id)),
|
|
16488
|
+
description: ext.description ?? ""
|
|
16286
16489
|
};
|
|
16287
|
-
if (ext.description !== void 0) row.description = ext.description;
|
|
16288
|
-
if (ext.stability !== void 0) row.stability = ext.stability;
|
|
16289
|
-
if (ext.preconditions !== void 0) row.preconditions = ext.preconditions;
|
|
16290
16490
|
if (ext.entry !== void 0) row.entry = ext.entry;
|
|
16291
16491
|
return row;
|
|
16292
16492
|
}
|
|
@@ -16688,9 +16888,7 @@ function renderBuiltInExtensionDetail(bundleId, ext, ansi) {
|
|
|
16688
16888
|
source: ansi.dim(PLUGINS_TEXTS.sourceBuiltIn)
|
|
16689
16889
|
});
|
|
16690
16890
|
const meta = { kind: ext.kind, version: ext.version };
|
|
16691
|
-
if (ext.
|
|
16692
|
-
if (ext.description !== void 0) meta.description = ext.description;
|
|
16693
|
-
if (ext.preconditions !== void 0) meta.preconditions = ext.preconditions;
|
|
16891
|
+
if (ext.description) meta.description = ext.description;
|
|
16694
16892
|
if (ext.entry !== void 0) meta.entry = ext.entry;
|
|
16695
16893
|
return header + "\n" + renderExtensionFields(meta);
|
|
16696
16894
|
}
|
|
@@ -16991,11 +17189,12 @@ function collectBuiltInApplicableKindWarnings(out, knownKinds) {
|
|
|
16991
17189
|
for (const ext of bundle.extensions) {
|
|
16992
17190
|
if (ext.kind !== "extractor") continue;
|
|
16993
17191
|
const extractor = ext;
|
|
16994
|
-
|
|
17192
|
+
const kinds = extractor.precondition?.kind;
|
|
17193
|
+
if (!kinds || kinds.length === 0) continue;
|
|
16995
17194
|
appendUnknownKindWarnings(
|
|
16996
17195
|
out,
|
|
16997
17196
|
qualifiedExtensionId(bundle.id, extractor.id),
|
|
16998
|
-
|
|
17197
|
+
kinds,
|
|
16999
17198
|
knownKinds
|
|
17000
17199
|
);
|
|
17001
17200
|
}
|
|
@@ -17005,19 +17204,24 @@ function collectUserApplicableKindWarnings(out, plugins, knownKinds) {
|
|
|
17005
17204
|
for (const p of plugins) {
|
|
17006
17205
|
if (p.status !== "enabled" || !p.extensions) continue;
|
|
17007
17206
|
for (const ext of p.extensions) {
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17207
|
+
collectKindsFromExtension(ext, knownKinds, out);
|
|
17208
|
+
}
|
|
17209
|
+
}
|
|
17210
|
+
}
|
|
17211
|
+
function collectKindsFromExtension(ext, knownKinds, out) {
|
|
17212
|
+
if (ext.kind !== "extractor") return;
|
|
17213
|
+
const inst = extensionInstance(ext);
|
|
17214
|
+
if (!inst) return;
|
|
17215
|
+
const pre = inst["precondition"];
|
|
17216
|
+
if (!pre || typeof pre !== "object") return;
|
|
17217
|
+
const kinds = pre.kind;
|
|
17218
|
+
if (!Array.isArray(kinds)) return;
|
|
17219
|
+
appendUnknownKindWarnings(
|
|
17220
|
+
out,
|
|
17221
|
+
qualifiedExtensionId(ext.pluginId, ext.id),
|
|
17222
|
+
kinds,
|
|
17223
|
+
knownKinds
|
|
17224
|
+
);
|
|
17021
17225
|
}
|
|
17022
17226
|
function appendUnknownKindWarnings(out, extractorQualifiedId, applicableKinds, knownKinds) {
|
|
17023
17227
|
for (const k of applicableKinds) {
|
|
@@ -17370,8 +17574,8 @@ function resolveBareToggle(id, catalogue, verb, ansi) {
|
|
|
17370
17574
|
}
|
|
17371
17575
|
|
|
17372
17576
|
// cli/commands/plugins/create.ts
|
|
17373
|
-
import { existsSync as
|
|
17374
|
-
import { join as
|
|
17577
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
17578
|
+
import { join as join16, resolve as resolve31 } from "path";
|
|
17375
17579
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
17376
17580
|
var PluginsCreateCommand = class extends SmCommand {
|
|
17377
17581
|
static paths = [["plugins", "create"]];
|
|
@@ -17397,8 +17601,8 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17397
17601
|
}
|
|
17398
17602
|
const ctx = defaultRuntimeContext();
|
|
17399
17603
|
const baseDir = defaultProjectPluginsDir(ctx);
|
|
17400
|
-
const targetDir = this.at ? resolve31(this.at) :
|
|
17401
|
-
if (
|
|
17604
|
+
const targetDir = this.at ? resolve31(this.at) : join16(baseDir, this.pluginId);
|
|
17605
|
+
if (existsSync22(targetDir) && !this.force) {
|
|
17402
17606
|
this.printer.error(
|
|
17403
17607
|
tx(PLUGINS_TEXTS.createRefuseOverwrite, {
|
|
17404
17608
|
glyph: errGlyph,
|
|
@@ -17407,14 +17611,15 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17407
17611
|
);
|
|
17408
17612
|
return ExitCode.Error;
|
|
17409
17613
|
}
|
|
17410
|
-
|
|
17614
|
+
const extractorName = `${this.pluginId}-extractor`;
|
|
17615
|
+
mkdirSync6(join16(targetDir, "extractors", extractorName), { recursive: true });
|
|
17411
17616
|
const specVersion = installedSpecVersion();
|
|
17412
17617
|
const manifest = {
|
|
17413
17618
|
id: this.pluginId,
|
|
17414
17619
|
version: "0.1.0",
|
|
17415
17620
|
specCompat: `^${specVersion}`,
|
|
17416
17621
|
catalogCompat: "^1.0.0",
|
|
17417
|
-
|
|
17622
|
+
granularity: "bundle",
|
|
17418
17623
|
description: "Generated by `sm plugins create`. Edit to taste.",
|
|
17419
17624
|
settings: {
|
|
17420
17625
|
keywords: {
|
|
@@ -17427,14 +17632,14 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17427
17632
|
}
|
|
17428
17633
|
};
|
|
17429
17634
|
writeFileSync3(
|
|
17430
|
-
|
|
17635
|
+
join16(targetDir, "plugin.json"),
|
|
17431
17636
|
JSON.stringify(manifest, null, 2) + "\n"
|
|
17432
17637
|
);
|
|
17433
17638
|
writeFileSync3(
|
|
17434
|
-
|
|
17435
|
-
scaffolderExtractorStub(
|
|
17639
|
+
join16(targetDir, "extractors", extractorName, "index.js"),
|
|
17640
|
+
scaffolderExtractorStub(extractorName)
|
|
17436
17641
|
);
|
|
17437
|
-
writeFileSync3(
|
|
17642
|
+
writeFileSync3(join16(targetDir, "README.md"), scaffolderReadme(this.pluginId));
|
|
17438
17643
|
this.printer.data(
|
|
17439
17644
|
tx(PLUGINS_TEXTS.createSuccess, {
|
|
17440
17645
|
targetDir: sanitizeForTerminal(targetDir),
|
|
@@ -17444,7 +17649,7 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17444
17649
|
return ExitCode.Ok;
|
|
17445
17650
|
}
|
|
17446
17651
|
};
|
|
17447
|
-
function scaffolderExtractorStub(
|
|
17652
|
+
function scaffolderExtractorStub(extractorId) {
|
|
17448
17653
|
return `/**
|
|
17449
17654
|
* Generated by \`sm plugins create\`. Edit the extract() body.
|
|
17450
17655
|
*
|
|
@@ -17453,6 +17658,11 @@ function scaffolderExtractorStub(pluginId) {
|
|
|
17453
17658
|
* splitting into a named export will surface as \`load-error: default
|
|
17454
17659
|
* export missing a string \\\`kind\\\` field\`.
|
|
17455
17660
|
*
|
|
17661
|
+
* Folder convention: this file lives at
|
|
17662
|
+
* \`extractors/${extractorId}/index.js\`. The bundle's plugin.json#/id
|
|
17663
|
+
* provides the qualified id \`<plugin-id>/${extractorId}\`; the loader
|
|
17664
|
+
* injects \`pluginId\` automatically, do NOT hardcode it here.
|
|
17665
|
+
*
|
|
17456
17666
|
* Declared view contributions (in plugin.json):
|
|
17457
17667
|
* - 'count' \u2192 slot \`card.footer.left\` (renders as a chip
|
|
17458
17668
|
* in the left footer of the node card)
|
|
@@ -17464,13 +17674,11 @@ function scaffolderExtractorStub(pluginId) {
|
|
|
17464
17674
|
* spec/view-slots.md
|
|
17465
17675
|
*/
|
|
17466
17676
|
export default {
|
|
17467
|
-
id: '${
|
|
17468
|
-
pluginId: '${pluginId}',
|
|
17677
|
+
id: '${extractorId}',
|
|
17469
17678
|
kind: 'extractor',
|
|
17470
17679
|
version: '0.1.0',
|
|
17471
17680
|
description: 'Counts configured keywords per node.',
|
|
17472
17681
|
stability: 'experimental',
|
|
17473
|
-
mode: 'deterministic',
|
|
17474
17682
|
emitsLinkKinds: [],
|
|
17475
17683
|
defaultConfidence: 'high',
|
|
17476
17684
|
scope: 'body',
|
|
@@ -17937,9 +18145,15 @@ var RefreshCommand = class extends SmCommand {
|
|
|
17937
18145
|
continue;
|
|
17938
18146
|
}
|
|
17939
18147
|
const fm = node.frontmatter ?? {};
|
|
17940
|
-
const applicable = allExtractors.filter(
|
|
17941
|
-
|
|
17942
|
-
|
|
18148
|
+
const applicable = allExtractors.filter((ex) => {
|
|
18149
|
+
const kinds = ex.precondition?.kind;
|
|
18150
|
+
if (!kinds || kinds.length === 0) return true;
|
|
18151
|
+
return kinds.some((qualified) => {
|
|
18152
|
+
const slashIdx = qualified.indexOf("/");
|
|
18153
|
+
const kindOnly = slashIdx === -1 ? qualified : qualified.slice(slashIdx + 1);
|
|
18154
|
+
return kindOnly === node.kind;
|
|
18155
|
+
});
|
|
18156
|
+
});
|
|
17943
18157
|
for (const extractor of applicable) {
|
|
17944
18158
|
const records = await runExtractorForEnrichment(extractor, node, body, fm);
|
|
17945
18159
|
for (const record of records) nodeEnrichments.push(record);
|
|
@@ -18049,7 +18263,7 @@ var SCAN_TEXTS = {
|
|
|
18049
18263
|
import { Command as Command30, Option as Option28 } from "clipanion";
|
|
18050
18264
|
|
|
18051
18265
|
// core/watcher/runtime.ts
|
|
18052
|
-
import { dirname as
|
|
18266
|
+
import { dirname as dirname17 } from "path";
|
|
18053
18267
|
|
|
18054
18268
|
// core/runtime/fresh-resolver.ts
|
|
18055
18269
|
async function buildFreshResolver(deps) {
|
|
@@ -18274,7 +18488,7 @@ function createWatcherRuntime(opts) {
|
|
|
18274
18488
|
roots: [
|
|
18275
18489
|
cwd,
|
|
18276
18490
|
// parent of `.skillmapignore`
|
|
18277
|
-
|
|
18491
|
+
dirname17(settingsPath)
|
|
18278
18492
|
// parent of `.skill-map/settings.json`
|
|
18279
18493
|
],
|
|
18280
18494
|
cwd,
|
|
@@ -18830,7 +19044,7 @@ function plural(count, word) {
|
|
|
18830
19044
|
}
|
|
18831
19045
|
|
|
18832
19046
|
// cli/commands/scan-compare.ts
|
|
18833
|
-
import { existsSync as
|
|
19047
|
+
import { existsSync as existsSync23, readFileSync as readFileSync18 } from "fs";
|
|
18834
19048
|
import { Command as Command32, Option as Option30 } from "clipanion";
|
|
18835
19049
|
var ScanCompareCommand = class extends SmCommand {
|
|
18836
19050
|
static paths = [["scan", "compare-with"]];
|
|
@@ -18942,12 +19156,12 @@ var ScanCompareCommand = class extends SmCommand {
|
|
|
18942
19156
|
}
|
|
18943
19157
|
};
|
|
18944
19158
|
function loadAndValidateDump(path) {
|
|
18945
|
-
if (!
|
|
19159
|
+
if (!existsSync23(path)) {
|
|
18946
19160
|
throw new Error(tx(SCAN_TEXTS.compareDumpNotFound, { path }));
|
|
18947
19161
|
}
|
|
18948
19162
|
let raw;
|
|
18949
19163
|
try {
|
|
18950
|
-
raw =
|
|
19164
|
+
raw = readFileSync18(path, "utf8");
|
|
18951
19165
|
} catch (err) {
|
|
18952
19166
|
const message = formatErrorMessage(err);
|
|
18953
19167
|
throw new Error(tx(SCAN_TEXTS.compareDumpReadFailed, { path, message }), { cause: err });
|
|
@@ -19072,7 +19286,7 @@ function renderDeltaIssues(issues) {
|
|
|
19072
19286
|
|
|
19073
19287
|
// cli/commands/serve.ts
|
|
19074
19288
|
import { spawn as spawn2 } from "child_process";
|
|
19075
|
-
import { existsSync as
|
|
19289
|
+
import { existsSync as existsSync27 } from "fs";
|
|
19076
19290
|
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
19077
19291
|
|
|
19078
19292
|
// cli/util/browser-launch.ts
|
|
@@ -19725,7 +19939,7 @@ function contentTypeFor(format) {
|
|
|
19725
19939
|
}
|
|
19726
19940
|
|
|
19727
19941
|
// server/health.ts
|
|
19728
|
-
import { existsSync as
|
|
19942
|
+
import { existsSync as existsSync24 } from "fs";
|
|
19729
19943
|
var FALLBACK_SCHEMA_VERSION = "1";
|
|
19730
19944
|
function buildHealth(deps) {
|
|
19731
19945
|
return {
|
|
@@ -19733,7 +19947,7 @@ function buildHealth(deps) {
|
|
|
19733
19947
|
schemaVersion: FALLBACK_SCHEMA_VERSION,
|
|
19734
19948
|
specVersion: deps.specVersion,
|
|
19735
19949
|
implVersion: VERSION,
|
|
19736
|
-
db:
|
|
19950
|
+
db: existsSync24(deps.dbPath) ? "present" : "missing",
|
|
19737
19951
|
cwd: deps.cwd,
|
|
19738
19952
|
dbPath: deps.dbPath
|
|
19739
19953
|
};
|
|
@@ -21097,7 +21311,8 @@ function invokeBump2(node, absPath, body) {
|
|
|
21097
21311
|
node,
|
|
21098
21312
|
nodeAbsolutePath: absPath,
|
|
21099
21313
|
invoker: "ui",
|
|
21100
|
-
now: () => /* @__PURE__ */ new Date()
|
|
21314
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
21315
|
+
settings: {}
|
|
21101
21316
|
});
|
|
21102
21317
|
}
|
|
21103
21318
|
function pickExistingVersion(node) {
|
|
@@ -21134,9 +21349,9 @@ function registerUpdateStatusRoute(app, deps) {
|
|
|
21134
21349
|
}
|
|
21135
21350
|
|
|
21136
21351
|
// server/static.ts
|
|
21137
|
-
import { existsSync as
|
|
21352
|
+
import { existsSync as existsSync25 } from "fs";
|
|
21138
21353
|
import { readFile as readFile5 } from "fs/promises";
|
|
21139
|
-
import { extname, join as
|
|
21354
|
+
import { extname, join as join17 } from "path";
|
|
21140
21355
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
21141
21356
|
var INDEX_HTML = "index.html";
|
|
21142
21357
|
var PLACEHOLDER_HTML = `<!doctype html>
|
|
@@ -21188,8 +21403,8 @@ function createSpaFallback(opts) {
|
|
|
21188
21403
|
return async (c, _next) => {
|
|
21189
21404
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
|
|
21190
21405
|
if (opts.uiDist === null) return htmlResponse(c, placeholder);
|
|
21191
|
-
const indexPath =
|
|
21192
|
-
if (!
|
|
21406
|
+
const indexPath = join17(opts.uiDist, INDEX_HTML);
|
|
21407
|
+
if (!existsSync25(indexPath)) return htmlResponse(c, placeholder);
|
|
21193
21408
|
return fileResponse(c, indexPath);
|
|
21194
21409
|
};
|
|
21195
21410
|
}
|
|
@@ -21758,10 +21973,10 @@ function validateNoUi(noUi, uiDist) {
|
|
|
21758
21973
|
}
|
|
21759
21974
|
|
|
21760
21975
|
// server/paths.ts
|
|
21761
|
-
import { existsSync as
|
|
21762
|
-
import { dirname as
|
|
21976
|
+
import { existsSync as existsSync26, statSync as statSync10 } from "fs";
|
|
21977
|
+
import { dirname as dirname18, isAbsolute as isAbsolute9, join as join18, resolve as resolve34 } from "path";
|
|
21763
21978
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
21764
|
-
var DEFAULT_UI_REL =
|
|
21979
|
+
var DEFAULT_UI_REL = join18("ui", "dist", "ui", "browser");
|
|
21765
21980
|
var PACKAGE_UI_REL = "ui";
|
|
21766
21981
|
var INDEX_HTML2 = "index.html";
|
|
21767
21982
|
function resolveDefaultUiDist(ctx) {
|
|
@@ -21773,10 +21988,10 @@ function resolveExplicitUiDist(ctx, raw) {
|
|
|
21773
21988
|
return isAbsolute9(raw) ? raw : resolve34(ctx.cwd, raw);
|
|
21774
21989
|
}
|
|
21775
21990
|
function isUiBundleDir(path) {
|
|
21776
|
-
if (!
|
|
21991
|
+
if (!existsSync26(path)) return false;
|
|
21777
21992
|
try {
|
|
21778
|
-
if (!
|
|
21779
|
-
return
|
|
21993
|
+
if (!statSync10(path).isDirectory()) return false;
|
|
21994
|
+
return existsSync26(join18(path, INDEX_HTML2));
|
|
21780
21995
|
} catch {
|
|
21781
21996
|
return false;
|
|
21782
21997
|
}
|
|
@@ -21784,7 +21999,7 @@ function isUiBundleDir(path) {
|
|
|
21784
21999
|
function resolvePackageBundledUi() {
|
|
21785
22000
|
let here;
|
|
21786
22001
|
try {
|
|
21787
|
-
here =
|
|
22002
|
+
here = dirname18(fileURLToPath5(import.meta.url));
|
|
21788
22003
|
} catch {
|
|
21789
22004
|
return null;
|
|
21790
22005
|
}
|
|
@@ -21793,11 +22008,11 @@ function resolvePackageBundledUi() {
|
|
|
21793
22008
|
function resolvePackageBundledUiFrom(here) {
|
|
21794
22009
|
let current = here;
|
|
21795
22010
|
for (let i = 0; i < 8; i++) {
|
|
21796
|
-
const candidate =
|
|
22011
|
+
const candidate = join18(current, PACKAGE_UI_REL);
|
|
21797
22012
|
if (isUiBundleDir(candidate)) return candidate;
|
|
21798
|
-
const distHere =
|
|
22013
|
+
const distHere = join18(current, "dist", PACKAGE_UI_REL);
|
|
21799
22014
|
if (isUiBundleDir(distHere)) return distHere;
|
|
21800
|
-
const parent =
|
|
22015
|
+
const parent = dirname18(current);
|
|
21801
22016
|
if (parent === current) return null;
|
|
21802
22017
|
current = parent;
|
|
21803
22018
|
}
|
|
@@ -21806,9 +22021,9 @@ function resolvePackageBundledUiFrom(here) {
|
|
|
21806
22021
|
function walkUpForUi(startDir) {
|
|
21807
22022
|
let current = resolve34(startDir);
|
|
21808
22023
|
for (let i = 0; i < 64; i++) {
|
|
21809
|
-
const candidate =
|
|
22024
|
+
const candidate = join18(current, DEFAULT_UI_REL);
|
|
21810
22025
|
if (isUiBundleDir(candidate)) return candidate;
|
|
21811
|
-
const parent =
|
|
22026
|
+
const parent = dirname18(current);
|
|
21812
22027
|
if (parent === current) return null;
|
|
21813
22028
|
current = parent;
|
|
21814
22029
|
}
|
|
@@ -22224,7 +22439,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
22224
22439
|
return ExitCode.Error;
|
|
22225
22440
|
}
|
|
22226
22441
|
const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
|
|
22227
|
-
if (this.db !== void 0 && !
|
|
22442
|
+
if (this.db !== void 0 && !existsSync27(dbPath)) {
|
|
22228
22443
|
this.printer.info(
|
|
22229
22444
|
tx(SERVE_TEXTS.dbNotFound, { path: sanitizeForTerminal(dbPath) })
|
|
22230
22445
|
);
|
|
@@ -22720,7 +22935,7 @@ function rankConfidenceForGrouping(c) {
|
|
|
22720
22935
|
}
|
|
22721
22936
|
|
|
22722
22937
|
// cli/commands/sidecar.ts
|
|
22723
|
-
import { existsSync as
|
|
22938
|
+
import { existsSync as existsSync28, unlinkSync as unlinkSync2 } from "fs";
|
|
22724
22939
|
import { resolve as resolve35 } from "path";
|
|
22725
22940
|
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
22726
22941
|
|
|
@@ -23161,7 +23376,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
23161
23376
|
return ExitCode.Error;
|
|
23162
23377
|
}
|
|
23163
23378
|
const sidecarAbsPath = sidecarPathFor(absPath);
|
|
23164
|
-
if (
|
|
23379
|
+
if (existsSync28(sidecarAbsPath) && this.force !== true) {
|
|
23165
23380
|
this.printer.error(
|
|
23166
23381
|
tx(SIDECAR_TEXTS.annotateExists, {
|
|
23167
23382
|
glyph: errGlyph,
|
|
@@ -23171,7 +23386,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
23171
23386
|
);
|
|
23172
23387
|
return ExitCode.Error;
|
|
23173
23388
|
}
|
|
23174
|
-
if (
|
|
23389
|
+
if (existsSync28(sidecarAbsPath) && this.force === true) {
|
|
23175
23390
|
try {
|
|
23176
23391
|
unlinkSync2(sidecarAbsPath);
|
|
23177
23392
|
} catch (err) {
|
|
@@ -23401,9 +23616,9 @@ var STUB_COMMANDS = [
|
|
|
23401
23616
|
];
|
|
23402
23617
|
|
|
23403
23618
|
// cli/commands/tutorial.ts
|
|
23404
|
-
import { existsSync as
|
|
23619
|
+
import { existsSync as existsSync29, readFileSync as readFileSync19 } from "fs";
|
|
23405
23620
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
23406
|
-
import { dirname as
|
|
23621
|
+
import { dirname as dirname19, join as join19, resolve as resolve36 } from "path";
|
|
23407
23622
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
23408
23623
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
23409
23624
|
|
|
@@ -23500,7 +23715,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
23500
23715
|
}
|
|
23501
23716
|
const variant = rawVariant ?? DEFAULT_VARIANT;
|
|
23502
23717
|
const spec = VARIANT_SPECS[variant];
|
|
23503
|
-
const target =
|
|
23718
|
+
const target = join19(ctx.cwd, spec.filename);
|
|
23504
23719
|
if (await pathExists(target) && !this.force) {
|
|
23505
23720
|
this.printer.error(
|
|
23506
23721
|
tx(TUTORIAL_TEXTS.alreadyExists, {
|
|
@@ -23574,7 +23789,7 @@ function loadBundledTutorialText(variant) {
|
|
|
23574
23789
|
}
|
|
23575
23790
|
function readTutorialFromDisk(variant) {
|
|
23576
23791
|
const spec = VARIANT_SPECS[variant];
|
|
23577
|
-
const here =
|
|
23792
|
+
const here = dirname19(fileURLToPath6(import.meta.url));
|
|
23578
23793
|
const candidates = [
|
|
23579
23794
|
// dev: src/cli/commands/ → repo-root .claude/skills/<slug>/SKILL.md
|
|
23580
23795
|
resolve36(here, "../../..", spec.sourcePath),
|
|
@@ -23584,8 +23799,8 @@ function readTutorialFromDisk(variant) {
|
|
|
23584
23799
|
resolve36(here, "../cli/tutorial", spec.bundledName)
|
|
23585
23800
|
];
|
|
23586
23801
|
for (const candidate of candidates) {
|
|
23587
|
-
if (
|
|
23588
|
-
return
|
|
23802
|
+
if (existsSync29(candidate)) {
|
|
23803
|
+
return readFileSync19(candidate, "utf8");
|
|
23589
23804
|
}
|
|
23590
23805
|
}
|
|
23591
23806
|
throw new Error(`SKILL.md not found in any candidate location (last tried: ${candidates[candidates.length - 1]})`);
|
|
@@ -23758,7 +23973,7 @@ await lifecycleDispatcher.dispatch(
|
|
|
23758
23973
|
process.exit(exitCode);
|
|
23759
23974
|
function resolveBareDefault() {
|
|
23760
23975
|
const ctx = defaultRuntimeContext();
|
|
23761
|
-
if (
|
|
23976
|
+
if (existsSync30(defaultProjectDbPath(ctx))) {
|
|
23762
23977
|
return ["serve"];
|
|
23763
23978
|
}
|
|
23764
23979
|
process.stderr.write(tx(ENTRY_TEXTS.bareNoProject, { cwd: ctx.cwd }));
|