@skill-map/cli 0.28.0 → 0.30.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 +1561 -1214
- 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-Y7MXGXU3.js → chunk-DZBSELHN.js} +1 -1
- 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-HOJFYUH4.js +123 -0
- package/dist/ui/{chunk-WOLLYGGL.js → chunk-KJQEO6P3.js} +1 -1
- package/dist/ui/chunk-LNRQ7VKE.js +1 -0
- 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/chunk-YQIWQVJ6.js +317 -0
- package/dist/ui/favicon-matrix.svg +15 -0
- package/dist/ui/index.html +12 -2
- package/dist/ui/main-X5YGJFU6.js +2 -0
- package/dist/ui/{styles-IKG3B6AM.css → styles-2WO3KNOY.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/ui/main-TZL26MZU.js +0 -2
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;
|
|
@@ -797,6 +775,53 @@ function link(source, target) {
|
|
|
797
775
|
};
|
|
798
776
|
}
|
|
799
777
|
|
|
778
|
+
// kernel/util/strip-code-blocks.ts
|
|
779
|
+
var FENCE_RE = /^(?<indent> {0,3})(?<fence>`{3,}|~{3,})/;
|
|
780
|
+
function stripCodeBlocks(input) {
|
|
781
|
+
if (!input) return input;
|
|
782
|
+
const fenceless = stripFences(input);
|
|
783
|
+
return stripInline(fenceless);
|
|
784
|
+
}
|
|
785
|
+
function stripFences(input) {
|
|
786
|
+
const out = [];
|
|
787
|
+
const lines = input.split("\n");
|
|
788
|
+
let openFence = null;
|
|
789
|
+
for (const line of lines) {
|
|
790
|
+
if (openFence) {
|
|
791
|
+
const closer = matchClosingFence(line, openFence);
|
|
792
|
+
if (closer) {
|
|
793
|
+
out.push(blank(line));
|
|
794
|
+
openFence = null;
|
|
795
|
+
} else {
|
|
796
|
+
out.push(blank(line));
|
|
797
|
+
}
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
const open = FENCE_RE.exec(line);
|
|
801
|
+
if (open?.groups) {
|
|
802
|
+
openFence = open.groups["fence"];
|
|
803
|
+
out.push(blank(line));
|
|
804
|
+
continue;
|
|
805
|
+
}
|
|
806
|
+
out.push(line);
|
|
807
|
+
}
|
|
808
|
+
return out.join("\n");
|
|
809
|
+
}
|
|
810
|
+
function matchClosingFence(line, openFence) {
|
|
811
|
+
const m = FENCE_RE.exec(line);
|
|
812
|
+
if (!m?.groups) return false;
|
|
813
|
+
const fence = m.groups["fence"];
|
|
814
|
+
return fence[0] === openFence[0] && fence.length >= openFence.length;
|
|
815
|
+
}
|
|
816
|
+
function stripInline(input) {
|
|
817
|
+
return input.replace(/(`+)([\s\S]*?)\1/g, (_full, ticks, body) => {
|
|
818
|
+
return ticks.replace(/`/g, " ") + blank(body) + ticks.replace(/`/g, " ");
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
function blank(s) {
|
|
822
|
+
return s.replace(/[^\s]/g, " ");
|
|
823
|
+
}
|
|
824
|
+
|
|
800
825
|
// kernel/trigger-normalize.ts
|
|
801
826
|
function normalizeTrigger(source) {
|
|
802
827
|
let out = source.normalize("NFD");
|
|
@@ -807,67 +832,51 @@ function normalizeTrigger(source) {
|
|
|
807
832
|
return out.trim();
|
|
808
833
|
}
|
|
809
834
|
|
|
810
|
-
//
|
|
811
|
-
var ID2 = "
|
|
812
|
-
var
|
|
813
|
-
var
|
|
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";
|
|
847
|
-
var AT_RE = /(?:^|[^A-Za-z0-9_@])(@[a-z0-9][a-z0-9_-]*(?:[/:][a-z0-9][a-z0-9_-]*)?)/gi;
|
|
835
|
+
// plugins/core/extractors/at-directive/index.ts
|
|
836
|
+
var ID2 = "at-directive";
|
|
837
|
+
var AT_RE = /(?:^|[^A-Za-z0-9_@])(@(?:\.{1,2}\/|\/)?[a-z0-9](?:[a-z0-9_\-./]*[a-z0-9_])?(?::[a-z0-9][a-z0-9_-]*)?)/gi;
|
|
838
|
+
var FILE_EXT_RE = /\.(md|mdx|js|jsx|ts|tsx|json|yml|yaml|toml|txt|html|css|scss|less|py|rb|go|rs|java|c|cpp|h|hpp|sh|sql|svg|png|jpg|jpeg|gif|webp|pdf)$/i;
|
|
848
839
|
var atDirectiveExtractor = {
|
|
849
|
-
id:
|
|
840
|
+
id: ID2,
|
|
850
841
|
pluginId: "core",
|
|
851
842
|
kind: "extractor",
|
|
852
843
|
version: "1.0.0",
|
|
853
|
-
description: "Detects
|
|
854
|
-
stability: "stable",
|
|
855
|
-
emitsLinkKinds: ["mentions"],
|
|
856
|
-
defaultConfidence: "medium",
|
|
844
|
+
description: "Detects `@<token>` directives in a node's body. A bare handle (e.g. `@team`) becomes a `mentions` link; a file-flavoured token (e.g. `@docs/api.md`, `@./readme.md`) becomes a `references` link, matching how Claude Code / Gemini CLI / Cursor read the same syntax.",
|
|
857
845
|
scope: "body",
|
|
858
846
|
extract(ctx) {
|
|
859
|
-
const
|
|
860
|
-
|
|
847
|
+
const seenMentions = /* @__PURE__ */ new Set();
|
|
848
|
+
const seenReferences = /* @__PURE__ */ new Set();
|
|
849
|
+
const body = stripCodeBlocks(ctx.body);
|
|
850
|
+
for (const match of body.matchAll(AT_RE)) {
|
|
861
851
|
const original = match[1];
|
|
852
|
+
const bare = original.slice(1);
|
|
853
|
+
const isReference = bare.startsWith("./") || bare.startsWith("../") || bare.startsWith("/") || FILE_EXT_RE.test(bare);
|
|
854
|
+
if (isReference) {
|
|
855
|
+
const target = bare.replace(/^\.\//, "");
|
|
856
|
+
if (seenReferences.has(target)) continue;
|
|
857
|
+
seenReferences.add(target);
|
|
858
|
+
ctx.emitLink({
|
|
859
|
+
source: ctx.node.path,
|
|
860
|
+
target,
|
|
861
|
+
kind: "references",
|
|
862
|
+
confidence: "medium",
|
|
863
|
+
sources: [ID2],
|
|
864
|
+
trigger: {
|
|
865
|
+
originalTrigger: original,
|
|
866
|
+
normalizedTrigger: target.toLowerCase()
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
862
871
|
const normalized = normalizeTrigger(original);
|
|
863
|
-
if (
|
|
864
|
-
|
|
872
|
+
if (seenMentions.has(normalized)) continue;
|
|
873
|
+
seenMentions.add(normalized);
|
|
865
874
|
ctx.emitLink({
|
|
866
875
|
source: ctx.node.path,
|
|
867
876
|
target: original,
|
|
868
877
|
kind: "mentions",
|
|
869
878
|
confidence: "medium",
|
|
870
|
-
sources: [
|
|
879
|
+
sources: [ID2],
|
|
871
880
|
trigger: {
|
|
872
881
|
originalTrigger: original,
|
|
873
882
|
normalizedTrigger: normalized
|
|
@@ -877,19 +886,16 @@ var atDirectiveExtractor = {
|
|
|
877
886
|
}
|
|
878
887
|
};
|
|
879
888
|
|
|
880
|
-
//
|
|
881
|
-
var
|
|
889
|
+
// plugins/core/extractors/external-url-counter/index.ts
|
|
890
|
+
var ID3 = "external-url-counter";
|
|
882
891
|
var URL_RE = /https?:\/\/[^\s<>"'`)\]]+/g;
|
|
883
892
|
var TRAILING_PUNCT = /[.,;:!?]+$/;
|
|
884
893
|
var externalUrlCounterExtractor = {
|
|
885
|
-
id:
|
|
894
|
+
id: ID3,
|
|
886
895
|
pluginId: "core",
|
|
887
896
|
kind: "extractor",
|
|
888
897
|
version: "1.0.0",
|
|
889
898
|
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
899
|
scope: "body",
|
|
894
900
|
/**
|
|
895
901
|
* Phase 6 / View contribution system, surface the distinct-URL
|
|
@@ -907,7 +913,7 @@ var externalUrlCounterExtractor = {
|
|
|
907
913
|
* inherited from the footer `.sm-gnode__stat` styles cloned by
|
|
908
914
|
* the `NodeCounter` renderer.
|
|
909
915
|
*/
|
|
910
|
-
|
|
916
|
+
ui: {
|
|
911
917
|
count: {
|
|
912
918
|
slot: "card.footer.left",
|
|
913
919
|
icon: "pi-link",
|
|
@@ -932,7 +938,7 @@ var externalUrlCounterExtractor = {
|
|
|
932
938
|
target: normalized,
|
|
933
939
|
kind: "references",
|
|
934
940
|
confidence: "low",
|
|
935
|
-
sources: [
|
|
941
|
+
sources: [ID3],
|
|
936
942
|
trigger: {
|
|
937
943
|
originalTrigger: original,
|
|
938
944
|
normalizedTrigger: normalized
|
|
@@ -977,20 +983,17 @@ function lineFor(lineStarts, offset) {
|
|
|
977
983
|
return lo + 1;
|
|
978
984
|
}
|
|
979
985
|
|
|
980
|
-
//
|
|
986
|
+
// plugins/core/extractors/markdown-link/index.ts
|
|
981
987
|
import { posix as pathPosix } from "path";
|
|
982
|
-
var
|
|
988
|
+
var ID4 = "markdown-link";
|
|
983
989
|
var LINK_RE = /(?<!!)\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
|
|
984
990
|
var URL_SCHEME_RE = /^[a-z][a-z0-9+.-]*:/i;
|
|
985
991
|
var markdownLinkExtractor = {
|
|
986
|
-
id:
|
|
992
|
+
id: ID4,
|
|
987
993
|
pluginId: "core",
|
|
988
994
|
kind: "extractor",
|
|
989
995
|
version: "1.0.0",
|
|
990
996
|
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
997
|
scope: "body",
|
|
995
998
|
extract(ctx) {
|
|
996
999
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1008,7 +1011,7 @@ var markdownLinkExtractor = {
|
|
|
1008
1011
|
target: resolved,
|
|
1009
1012
|
kind: "references",
|
|
1010
1013
|
confidence: "high",
|
|
1011
|
-
sources: [
|
|
1014
|
+
sources: [ID4],
|
|
1012
1015
|
trigger: {
|
|
1013
1016
|
originalTrigger: original,
|
|
1014
1017
|
normalizedTrigger: resolved
|
|
@@ -1047,99 +1050,54 @@ function lineFor2(lineStarts, offset) {
|
|
|
1047
1050
|
return lo + 1;
|
|
1048
1051
|
}
|
|
1049
1052
|
|
|
1050
|
-
//
|
|
1051
|
-
var
|
|
1052
|
-
var
|
|
1053
|
-
var
|
|
1054
|
-
|
|
1055
|
-
id: ID6,
|
|
1053
|
+
// plugins/core/extractors/slash/index.ts
|
|
1054
|
+
var ID5 = "slash";
|
|
1055
|
+
var SLASH_RE = /(?<![A-Za-z0-9_/.:?#])(\/[a-z0-9][a-z0-9_-]*(?::[a-z0-9][a-z0-9_-]*)?)/gi;
|
|
1056
|
+
var slashExtractor = {
|
|
1057
|
+
id: ID5,
|
|
1056
1058
|
pluginId: "core",
|
|
1057
|
-
kind: "
|
|
1059
|
+
kind: "extractor",
|
|
1058
1060
|
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
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
-
}
|
|
1061
|
+
description: "Detects `/command` invocations in a node's body and turns each one into an arrow between nodes in the graph.",
|
|
1062
|
+
scope: "body",
|
|
1063
|
+
extract(ctx) {
|
|
1064
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1065
|
+
const body = stripCodeBlocks(ctx.body);
|
|
1066
|
+
for (const match of body.matchAll(SLASH_RE)) {
|
|
1067
|
+
const original = match[1];
|
|
1068
|
+
const endIdx = (match.index ?? 0) + match[0].length;
|
|
1069
|
+
const nextChar = body[endIdx];
|
|
1070
|
+
if (nextChar && /[A-Za-z0-9_/-]/.test(nextChar)) continue;
|
|
1071
|
+
const normalized = normalizeTrigger(original);
|
|
1072
|
+
if (seen.has(normalized)) continue;
|
|
1073
|
+
seen.add(normalized);
|
|
1074
|
+
ctx.emitLink({
|
|
1075
|
+
source: ctx.node.path,
|
|
1076
|
+
target: original,
|
|
1077
|
+
kind: "invokes",
|
|
1078
|
+
confidence: "medium",
|
|
1079
|
+
sources: [ID5],
|
|
1080
|
+
trigger: {
|
|
1081
|
+
originalTrigger: original,
|
|
1082
|
+
normalizedTrigger: normalized
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1108
1085
|
}
|
|
1109
|
-
return issues;
|
|
1110
1086
|
}
|
|
1111
1087
|
};
|
|
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
1088
|
|
|
1128
|
-
//
|
|
1129
|
-
var
|
|
1089
|
+
// plugins/core/extractors/tools-count/index.ts
|
|
1090
|
+
var ID6 = "tools-count";
|
|
1130
1091
|
var TOOLTIP_MAX = 255;
|
|
1131
1092
|
var toolsCountExtractor = {
|
|
1132
|
-
id:
|
|
1093
|
+
id: ID6,
|
|
1133
1094
|
pluginId: "core",
|
|
1134
1095
|
kind: "extractor",
|
|
1135
1096
|
version: "1.0.0",
|
|
1136
1097
|
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
1098
|
scope: "frontmatter",
|
|
1141
|
-
|
|
1142
|
-
|
|
1099
|
+
precondition: { kind: ["claude/agent"] },
|
|
1100
|
+
ui: {
|
|
1143
1101
|
count: {
|
|
1144
1102
|
slot: "card.footer.left",
|
|
1145
1103
|
icon: "pi-wrench",
|
|
@@ -1168,165 +1126,148 @@ function buildTooltip(names) {
|
|
|
1168
1126
|
return `${joined.slice(0, TOOLTIP_MAX - 1)}\u2026`;
|
|
1169
1127
|
}
|
|
1170
1128
|
|
|
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}}"
|
|
1129
|
+
// plugins/core/analyzers/annotation-orphan/text.ts
|
|
1130
|
+
var ANNOTATION_ORPHAN_TEXTS = {
|
|
1131
|
+
/** Sidecar `<path>.sm` has no matching `<path>.md`. */
|
|
1132
|
+
message: "Orphan sidecar: {{sidecarPath}} has no matching markdown node at {{expectedMdPath}}."
|
|
1194
1133
|
};
|
|
1195
1134
|
|
|
1196
|
-
//
|
|
1197
|
-
var
|
|
1198
|
-
var
|
|
1199
|
-
|
|
1200
|
-
"skill",
|
|
1201
|
-
"agent"
|
|
1202
|
-
]);
|
|
1203
|
-
var triggerCollisionAnalyzer = {
|
|
1204
|
-
id: ID8,
|
|
1135
|
+
// plugins/core/analyzers/annotation-orphan/index.ts
|
|
1136
|
+
var ID7 = "annotation-orphan";
|
|
1137
|
+
var annotationOrphanAnalyzer = {
|
|
1138
|
+
id: ID7,
|
|
1205
1139
|
pluginId: "core",
|
|
1206
1140
|
kind: "analyzer",
|
|
1207
|
-
mode: "deterministic",
|
|
1208
1141
|
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
|
|
1142
|
+
description: "Detects and flags sidecars (`.sm`) whose `.md` no longer exists.",
|
|
1143
|
+
mode: "deterministic",
|
|
1214
1144
|
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
|
-
}
|
|
1145
|
+
const orphans = ctx.orphanSidecars;
|
|
1146
|
+
if (!orphans || orphans.length === 0) return [];
|
|
1243
1147
|
const issues = [];
|
|
1244
|
-
for (const
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1148
|
+
for (const orphan of orphans) {
|
|
1149
|
+
const expectedMdRelative = orphan.relativePath.endsWith(".sm") ? `${orphan.relativePath.slice(0, -".sm".length)}.md` : `${orphan.relativePath}.md`;
|
|
1150
|
+
issues.push({
|
|
1151
|
+
analyzerId: ID7,
|
|
1152
|
+
severity: "warn",
|
|
1153
|
+
nodeIds: [expectedMdRelative],
|
|
1154
|
+
message: tx(ANNOTATION_ORPHAN_TEXTS.message, {
|
|
1155
|
+
sidecarPath: orphan.relativePath,
|
|
1156
|
+
expectedMdPath: orphan.expectedMdPath
|
|
1157
|
+
}),
|
|
1158
|
+
data: {
|
|
1159
|
+
sidecarPath: orphan.relativePath,
|
|
1160
|
+
expectedMdPath: orphan.expectedMdPath
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1247
1163
|
}
|
|
1248
1164
|
return issues;
|
|
1249
1165
|
}
|
|
1250
1166
|
};
|
|
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
|
-
|
|
1167
|
+
|
|
1168
|
+
// plugins/core/analyzers/annotation-stale/text.ts
|
|
1169
|
+
var ANNOTATION_STALE_TEXTS = {
|
|
1170
|
+
/** body changed since last bump */
|
|
1171
|
+
bodyDrift: "{{path}}: sidecar `.sm` is stale (body changed since last bump).",
|
|
1172
|
+
/** frontmatter changed since last bump */
|
|
1173
|
+
frontmatterDrift: "{{path}}: sidecar `.sm` is stale (frontmatter changed since last bump).",
|
|
1174
|
+
/** both body and frontmatter changed */
|
|
1175
|
+
bothDrift: "{{path}}: sidecar `.sm` is stale (body and frontmatter changed since last bump).",
|
|
1176
|
+
// Tooltips for the `card.footer.right` clock chip emitted alongside
|
|
1177
|
+
// the issue. Lists only the drifted face(s), in-sync faces are
|
|
1178
|
+
// omitted so the operator immediately sees what's modified without
|
|
1179
|
+
// scanning prose. No `{{path}}` placeholder, the chip already sits
|
|
1180
|
+
// on the affected node. The hint `sm bump <path>` keeps `<path>` as
|
|
1181
|
+
// a literal placeholder the operator substitutes.
|
|
1182
|
+
bodyTooltip: "Sidecar drift since last bump:\n \u2022 body\nRun `sm bump <path>` to refresh.",
|
|
1183
|
+
frontmatterTooltip: "Sidecar drift since last bump:\n \u2022 frontmatter\nRun `sm bump <path>` to refresh.",
|
|
1184
|
+
bothTooltip: "Sidecar drift since last bump:\n \u2022 body\n \u2022 frontmatter\nRun `sm bump <path>` to refresh."
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
// plugins/core/analyzers/annotation-stale/index.ts
|
|
1188
|
+
var ID8 = "annotation-stale";
|
|
1189
|
+
var annotationStaleAnalyzer = {
|
|
1190
|
+
id: ID8,
|
|
1191
|
+
pluginId: "core",
|
|
1192
|
+
kind: "analyzer",
|
|
1193
|
+
version: "1.0.0",
|
|
1194
|
+
description: "Detects and marks sidecars (`.sm`) out of date of their `.md`.",
|
|
1195
|
+
mode: "deterministic",
|
|
1196
|
+
// The natural fix is to bump the node: refreshes `for` hashes,
|
|
1197
|
+
// increments `annotations.version`, and stamps the audit block. The
|
|
1198
|
+
// UI surfaces `core/bump` in the node inspector under "Recommended
|
|
1199
|
+
// for issues" whenever this analyzer fires.
|
|
1200
|
+
ui: {
|
|
1201
|
+
// A `pi-clock` chip in the footer-right cluster so the operator
|
|
1202
|
+
// spots drift in the list / inspector view (and on the graph card
|
|
1203
|
+
// body). Emitted with `value: 0` and `emitWhenEmpty: true` so the
|
|
1204
|
+
// renderer treats it as icon-only, drift severity is binary at
|
|
1205
|
+
// this surface (the tooltip carries the per-face detail body /
|
|
1206
|
+
// frontmatter / both). The corner badge on `graph.node.alert` was
|
|
1207
|
+
// dropped on purpose: a tooltip on the footer chip is enough, and
|
|
1208
|
+
// the corner badge stacked on top of broken-ref / unknown-field
|
|
1209
|
+
// alerts produced visual noise.
|
|
1210
|
+
staleIcon: {
|
|
1211
|
+
slot: "card.footer.right",
|
|
1212
|
+
icon: "pi-clock",
|
|
1213
|
+
emitWhenEmpty: true,
|
|
1214
|
+
priority: 20
|
|
1215
|
+
}
|
|
1216
|
+
},
|
|
1217
|
+
evaluate(ctx) {
|
|
1218
|
+
const issues = [];
|
|
1219
|
+
for (const node of ctx.nodes) {
|
|
1220
|
+
const status = node.sidecar?.status;
|
|
1221
|
+
if (status === void 0 || status === null) continue;
|
|
1222
|
+
if (status === "fresh") continue;
|
|
1223
|
+
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 });
|
|
1224
|
+
issues.push({
|
|
1225
|
+
analyzerId: ID8,
|
|
1226
|
+
severity: "warn",
|
|
1227
|
+
nodeIds: [node.path],
|
|
1228
|
+
message,
|
|
1229
|
+
data: { status }
|
|
1230
|
+
});
|
|
1231
|
+
ctx.emitContribution(node.path, "staleIcon", {
|
|
1232
|
+
value: 0,
|
|
1233
|
+
severity: "warn",
|
|
1234
|
+
tooltip: tooltipFor(status)
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
return issues;
|
|
1278
1238
|
}
|
|
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
|
-
);
|
|
1239
|
+
};
|
|
1240
|
+
function tooltipFor(status) {
|
|
1241
|
+
switch (status) {
|
|
1242
|
+
case "stale-body":
|
|
1243
|
+
return ANNOTATION_STALE_TEXTS.bodyTooltip;
|
|
1244
|
+
case "stale-frontmatter":
|
|
1245
|
+
return ANNOTATION_STALE_TEXTS.frontmatterTooltip;
|
|
1246
|
+
case "stale-both":
|
|
1247
|
+
return ANNOTATION_STALE_TEXTS.bothTooltip;
|
|
1294
1248
|
}
|
|
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
1249
|
}
|
|
1315
1250
|
|
|
1316
|
-
//
|
|
1317
|
-
import { resolve } from "path";
|
|
1251
|
+
// plugins/core/analyzers/broken-ref/index.ts
|
|
1252
|
+
import { posix as pathPosix2, resolve } from "path";
|
|
1318
1253
|
|
|
1319
|
-
//
|
|
1254
|
+
// plugins/core/analyzers/broken-ref/text.ts
|
|
1320
1255
|
var BROKEN_REF_TEXTS = {
|
|
1321
1256
|
/** `Broken <kind> reference from <source> → <target>` */
|
|
1322
1257
|
message: "Broken {{kind}} reference from {{source}} \u2192 {{target}}",
|
|
1323
1258
|
// Tooltips for the per-node view-contribution badges. Singular vs
|
|
1324
1259
|
// plural keeps the count grammar correct without a sub-template.
|
|
1325
1260
|
alertTooltipSingle: "This node has a broken reference. Open the inspector for details.",
|
|
1326
|
-
alertTooltipMany: "This node has {{count}} broken references. Open the inspector for details."
|
|
1261
|
+
alertTooltipMany: "This node has {{count}} broken references. Open the inspector for details.",
|
|
1262
|
+
// Fix-summary copy when the broken trigger has a same-named file on
|
|
1263
|
+
// disk that does not advertise `name:` in its frontmatter. Two
|
|
1264
|
+
// variants for single vs multiple candidates; same template family
|
|
1265
|
+
// as the alert tooltips above.
|
|
1266
|
+
hintSummarySingle: "Add `name: {{name}}` to the frontmatter of {{candidate}} so this reference resolves.",
|
|
1267
|
+
hintSummaryMany: "Add `name: {{name}}` to the frontmatter of one of these files so this reference resolves: {{candidates}}."
|
|
1327
1268
|
};
|
|
1328
1269
|
|
|
1329
|
-
//
|
|
1270
|
+
// plugins/core/analyzers/broken-ref/index.ts
|
|
1330
1271
|
var ID9 = "broken-ref";
|
|
1331
1272
|
var brokenRefAnalyzer = {
|
|
1332
1273
|
id: ID9,
|
|
@@ -1334,9 +1275,8 @@ var brokenRefAnalyzer = {
|
|
|
1334
1275
|
kind: "analyzer",
|
|
1335
1276
|
version: "1.0.0",
|
|
1336
1277
|
description: "Detects and flags arrows pointing at a node not part of the current scan.",
|
|
1337
|
-
stability: "stable",
|
|
1338
1278
|
mode: "deterministic",
|
|
1339
|
-
|
|
1279
|
+
ui: {
|
|
1340
1280
|
// Corner badge on the graph card; count omitted when there is a
|
|
1341
1281
|
// single broken ref (avoids a noisy "icon + 1" chip).
|
|
1342
1282
|
alert: {
|
|
@@ -1364,13 +1304,15 @@ var brokenRefAnalyzer = {
|
|
|
1364
1304
|
evaluate(ctx) {
|
|
1365
1305
|
const byPath3 = new Set(ctx.nodes.map((n) => n.path));
|
|
1366
1306
|
const byNormalizedName = indexByNormalizedName(ctx.nodes);
|
|
1307
|
+
const byBasenameWithoutName = indexByBasenameWithoutName(ctx.nodes);
|
|
1367
1308
|
const refIndex = ctx.referenceablePaths && ctx.referenceablePaths.size > 0 && ctx.cwd ? { paths: ctx.referenceablePaths, cwd: ctx.cwd } : null;
|
|
1368
1309
|
const issues = [];
|
|
1369
1310
|
const perNode = /* @__PURE__ */ new Map();
|
|
1370
1311
|
for (const link2 of ctx.links) {
|
|
1371
1312
|
if (isResolved(link2, byPath3, byNormalizedName)) continue;
|
|
1372
1313
|
if (refIndex && resolvesViaReferencePaths(link2, refIndex)) continue;
|
|
1373
|
-
|
|
1314
|
+
const candidates = findHintCandidates(link2, byBasenameWithoutName);
|
|
1315
|
+
issues.push(buildIssue(link2, candidates));
|
|
1374
1316
|
perNode.set(link2.source, (perNode.get(link2.source) ?? 0) + 1);
|
|
1375
1317
|
}
|
|
1376
1318
|
for (const [nodePath, count] of perNode) {
|
|
@@ -1390,8 +1332,13 @@ var brokenRefAnalyzer = {
|
|
|
1390
1332
|
return issues;
|
|
1391
1333
|
}
|
|
1392
1334
|
};
|
|
1393
|
-
function buildIssue(link2) {
|
|
1394
|
-
|
|
1335
|
+
function buildIssue(link2, hintCandidates = []) {
|
|
1336
|
+
const data = {
|
|
1337
|
+
target: link2.target,
|
|
1338
|
+
kind: link2.kind,
|
|
1339
|
+
trigger: link2.trigger?.normalizedTrigger ?? null
|
|
1340
|
+
};
|
|
1341
|
+
const issue = {
|
|
1395
1342
|
analyzerId: ID9,
|
|
1396
1343
|
severity: "warn",
|
|
1397
1344
|
nodeIds: [link2.source],
|
|
@@ -1400,12 +1347,28 @@ function buildIssue(link2) {
|
|
|
1400
1347
|
source: link2.source,
|
|
1401
1348
|
target: link2.target
|
|
1402
1349
|
}),
|
|
1403
|
-
data
|
|
1404
|
-
target: link2.target,
|
|
1405
|
-
kind: link2.kind,
|
|
1406
|
-
trigger: link2.trigger?.normalizedTrigger ?? null
|
|
1407
|
-
}
|
|
1350
|
+
data
|
|
1408
1351
|
};
|
|
1352
|
+
if (hintCandidates.length > 0) {
|
|
1353
|
+
const suggestedName = (link2.trigger?.normalizedTrigger ?? "").replace(/^[/@]/, "").trim();
|
|
1354
|
+
const candidatePaths = hintCandidates.map((n) => n.path);
|
|
1355
|
+
data["hint"] = {
|
|
1356
|
+
kind: "missing-frontmatter-name",
|
|
1357
|
+
suggestedName,
|
|
1358
|
+
candidates: candidatePaths
|
|
1359
|
+
};
|
|
1360
|
+
issue.fix = {
|
|
1361
|
+
summary: candidatePaths.length === 1 ? tx(BROKEN_REF_TEXTS.hintSummarySingle, {
|
|
1362
|
+
name: suggestedName,
|
|
1363
|
+
candidate: candidatePaths[0]
|
|
1364
|
+
}) : tx(BROKEN_REF_TEXTS.hintSummaryMany, {
|
|
1365
|
+
name: suggestedName,
|
|
1366
|
+
candidates: candidatePaths.join(", ")
|
|
1367
|
+
}),
|
|
1368
|
+
autofixable: false
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
return issue;
|
|
1409
1372
|
}
|
|
1410
1373
|
function resolvesViaReferencePaths(link2, refIndex) {
|
|
1411
1374
|
if (!isPathStyleLink(link2)) return false;
|
|
@@ -1424,6 +1387,36 @@ function indexByNormalizedName(nodes) {
|
|
|
1424
1387
|
}
|
|
1425
1388
|
return out;
|
|
1426
1389
|
}
|
|
1390
|
+
function basenameWithoutExt(path) {
|
|
1391
|
+
const base = pathPosix2.basename(path);
|
|
1392
|
+
const ext = pathPosix2.extname(base);
|
|
1393
|
+
return ext ? base.slice(0, -ext.length) : base;
|
|
1394
|
+
}
|
|
1395
|
+
function indexByBasenameWithoutName(nodes) {
|
|
1396
|
+
const out = /* @__PURE__ */ new Map();
|
|
1397
|
+
for (const node of nodes) {
|
|
1398
|
+
const raw = node.frontmatter?.["name"];
|
|
1399
|
+
const name = typeof raw === "string" ? raw : "";
|
|
1400
|
+
if (name) continue;
|
|
1401
|
+
const bare = basenameWithoutExt(node.path);
|
|
1402
|
+
if (!bare) continue;
|
|
1403
|
+
const key = normalizeTrigger(bare);
|
|
1404
|
+
if (!key) continue;
|
|
1405
|
+
const bucket = out.get(key) ?? [];
|
|
1406
|
+
bucket.push(node);
|
|
1407
|
+
out.set(key, bucket);
|
|
1408
|
+
}
|
|
1409
|
+
return out;
|
|
1410
|
+
}
|
|
1411
|
+
function findHintCandidates(link2, idx) {
|
|
1412
|
+
const normalized = link2.trigger?.normalizedTrigger;
|
|
1413
|
+
if (!normalized) return [];
|
|
1414
|
+
const sigil = normalized.charAt(0);
|
|
1415
|
+
if (sigil !== "/" && sigil !== "@") return [];
|
|
1416
|
+
const withoutSigil = normalized.slice(1).trim();
|
|
1417
|
+
if (!withoutSigil) return [];
|
|
1418
|
+
return idx.get(withoutSigil) ?? [];
|
|
1419
|
+
}
|
|
1427
1420
|
function isResolved(link2, byPath3, byNormalizedName) {
|
|
1428
1421
|
const normalized = link2.trigger?.normalizedTrigger;
|
|
1429
1422
|
if (normalized) {
|
|
@@ -1439,66 +1432,69 @@ function isPathStyleLink(link2) {
|
|
|
1439
1432
|
return true;
|
|
1440
1433
|
}
|
|
1441
1434
|
|
|
1442
|
-
//
|
|
1443
|
-
var
|
|
1444
|
-
|
|
1445
|
-
|
|
1435
|
+
// plugins/core/analyzers/contribution-orphan/index.ts
|
|
1436
|
+
var ID10 = "contribution-orphan";
|
|
1437
|
+
var contributionOrphanAnalyzer = {
|
|
1438
|
+
id: ID10,
|
|
1439
|
+
pluginId: "core",
|
|
1440
|
+
kind: "analyzer",
|
|
1441
|
+
version: "0.0.0",
|
|
1442
|
+
description: "Detects and warns about plugin data referencing nodes renamed or deleted in the latest scan.",
|
|
1443
|
+
mode: "deterministic",
|
|
1444
|
+
evaluate(_ctx) {
|
|
1445
|
+
return [];
|
|
1446
|
+
}
|
|
1446
1447
|
};
|
|
1447
1448
|
|
|
1448
|
-
//
|
|
1449
|
-
var
|
|
1450
|
-
|
|
1451
|
-
|
|
1449
|
+
// plugins/core/analyzers/job-orphan-file/text.ts
|
|
1450
|
+
var JOB_ORPHAN_FILE_TEXTS = {
|
|
1451
|
+
/**
|
|
1452
|
+
* `<path>.md` lives under `.skill-map/jobs/` but no `state_jobs.filePath`
|
|
1453
|
+
* row references it. Run `sm job prune --orphan-files` to remove.
|
|
1454
|
+
*/
|
|
1455
|
+
message: "Orphan job file: {{filePath}} is not referenced by any state_jobs row. Run `sm job prune --orphan-files` to remove it."
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
// plugins/core/analyzers/job-orphan-file/index.ts
|
|
1459
|
+
var ID11 = "job-orphan-file";
|
|
1460
|
+
var jobOrphanFileAnalyzer = {
|
|
1461
|
+
id: ID11,
|
|
1452
1462
|
pluginId: "core",
|
|
1453
1463
|
kind: "analyzer",
|
|
1454
1464
|
version: "1.0.0",
|
|
1455
|
-
description: "Detects and
|
|
1456
|
-
stability: "stable",
|
|
1465
|
+
description: "Detects and flags leftover job result files (no live job references them). Cleanup via `sm job prune --orphan-files`.",
|
|
1457
1466
|
mode: "deterministic",
|
|
1458
1467
|
evaluate(ctx) {
|
|
1468
|
+
const orphans = ctx.orphanJobFiles;
|
|
1469
|
+
if (!orphans || orphans.length === 0) return [];
|
|
1459
1470
|
const issues = [];
|
|
1460
|
-
for (const
|
|
1461
|
-
const supersededBy = pickSupersededBy(node);
|
|
1462
|
-
if (supersededBy === null) continue;
|
|
1471
|
+
for (const filePath of orphans) {
|
|
1463
1472
|
issues.push({
|
|
1464
|
-
analyzerId:
|
|
1465
|
-
severity: "
|
|
1466
|
-
nodeIds: [
|
|
1467
|
-
message: tx(
|
|
1468
|
-
|
|
1469
|
-
supersededBy
|
|
1470
|
-
}),
|
|
1471
|
-
data: { supersededBy }
|
|
1473
|
+
analyzerId: ID11,
|
|
1474
|
+
severity: "warn",
|
|
1475
|
+
nodeIds: [filePath],
|
|
1476
|
+
message: tx(JOB_ORPHAN_FILE_TEXTS.message, { filePath }),
|
|
1477
|
+
data: { filePath }
|
|
1472
1478
|
});
|
|
1473
1479
|
}
|
|
1474
1480
|
return issues;
|
|
1475
1481
|
}
|
|
1476
1482
|
};
|
|
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
1483
|
|
|
1487
|
-
//
|
|
1484
|
+
// plugins/core/analyzers/link-conflict/text.ts
|
|
1488
1485
|
var LINK_CONFLICT_TEXTS = {
|
|
1489
1486
|
/** `Detectors disagree on link kind for <source> → <target> (<kindList>)` */
|
|
1490
1487
|
message: "Detectors disagree on link kind for {{source}} \u2192 {{target}} ({{kindList}})"
|
|
1491
1488
|
};
|
|
1492
1489
|
|
|
1493
|
-
//
|
|
1494
|
-
var
|
|
1490
|
+
// plugins/core/analyzers/link-conflict/index.ts
|
|
1491
|
+
var ID12 = "link-conflict";
|
|
1495
1492
|
var linkConflictAnalyzer = {
|
|
1496
|
-
id:
|
|
1493
|
+
id: ID12,
|
|
1497
1494
|
pluginId: "core",
|
|
1498
1495
|
kind: "analyzer",
|
|
1499
1496
|
version: "1.0.0",
|
|
1500
1497
|
description: 'Detects and flags conflicting arrow meanings between extractors (e.g. "references" vs "invokes").',
|
|
1501
|
-
stability: "stable",
|
|
1502
1498
|
mode: "deterministic",
|
|
1503
1499
|
// Bucket links by (source, target), then per-bucket detect distinct
|
|
1504
1500
|
// kinds. The branching is intrinsic to the per-bucket conflict
|
|
@@ -1542,7 +1538,7 @@ var linkConflictAnalyzer = {
|
|
|
1542
1538
|
const [source, target] = key.split("\0");
|
|
1543
1539
|
const kindList = variants.map((v) => v.kind).join(" / ");
|
|
1544
1540
|
issues.push({
|
|
1545
|
-
analyzerId:
|
|
1541
|
+
analyzerId: ID12,
|
|
1546
1542
|
severity: "warn",
|
|
1547
1543
|
nodeIds: [source, target],
|
|
1548
1544
|
message: tx(LINK_CONFLICT_TEXTS.message, {
|
|
@@ -1567,168 +1563,386 @@ function rankConfidence(c) {
|
|
|
1567
1563
|
}
|
|
1568
1564
|
}
|
|
1569
1565
|
|
|
1570
|
-
//
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1566
|
+
// kernel/util/trigger-resolve.ts
|
|
1567
|
+
function buildNameIndex(nodes) {
|
|
1568
|
+
const out = /* @__PURE__ */ new Map();
|
|
1569
|
+
indexByCanonicalName(nodes, out);
|
|
1570
|
+
fillIndexWithPathBasename(nodes, out);
|
|
1571
|
+
return out;
|
|
1572
|
+
}
|
|
1573
|
+
function indexByCanonicalName(nodes, out) {
|
|
1574
|
+
for (const node of nodes) {
|
|
1575
|
+
const raw = canonicalName(node);
|
|
1576
|
+
if (raw === null) continue;
|
|
1577
|
+
const key = normalizeTrigger(raw);
|
|
1578
|
+
if (!out.has(key)) out.set(key, node.path);
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
function fillIndexWithPathBasename(nodes, out) {
|
|
1582
|
+
for (const node of nodes) {
|
|
1583
|
+
if (canonicalName(node) !== null) continue;
|
|
1584
|
+
const derived = pathBasenameForLink(node.path);
|
|
1585
|
+
if (derived.length === 0) continue;
|
|
1586
|
+
const key = normalizeTrigger(derived);
|
|
1587
|
+
if (!out.has(key)) out.set(key, node.path);
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
function canonicalName(node) {
|
|
1591
|
+
const raw = node.frontmatter?.["name"];
|
|
1592
|
+
if (typeof raw !== "string" || raw.length === 0) return null;
|
|
1593
|
+
return raw;
|
|
1594
|
+
}
|
|
1595
|
+
function pathBasenameForLink(path) {
|
|
1596
|
+
const segments = path.split("/").filter((s) => s.length > 0);
|
|
1597
|
+
if (segments.length === 0) return path;
|
|
1598
|
+
const last = segments[segments.length - 1];
|
|
1599
|
+
if (last === "SKILL.md" && segments.length >= 2) {
|
|
1600
|
+
return segments[segments.length - 2];
|
|
1601
|
+
}
|
|
1602
|
+
return last.replace(/\.md$/, "");
|
|
1603
|
+
}
|
|
1604
|
+
function resolveLinkTargetToPath(link2, nameIndex) {
|
|
1605
|
+
const raw = link2.target;
|
|
1606
|
+
const sigil = raw.charAt(0);
|
|
1607
|
+
if (sigil !== "/" && sigil !== "@") return raw;
|
|
1608
|
+
const normalizedTrigger = link2.trigger?.normalizedTrigger;
|
|
1609
|
+
const normalized = typeof normalizedTrigger === "string" ? normalizedTrigger.replace(/^[/@]/, "").trim() : normalizeTrigger(raw.slice(1));
|
|
1610
|
+
const resolved = nameIndex.get(normalized);
|
|
1611
|
+
return resolved ?? raw;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// plugins/core/analyzers/link-counts/index.ts
|
|
1615
|
+
var ID13 = "link-counts";
|
|
1616
|
+
var linkCountsAnalyzer = {
|
|
1617
|
+
id: ID13,
|
|
1618
|
+
pluginId: "core",
|
|
1619
|
+
kind: "analyzer",
|
|
1620
|
+
version: "1.0.0",
|
|
1621
|
+
description: "Counts incoming and outgoing links per node.",
|
|
1622
|
+
mode: "deterministic",
|
|
1623
|
+
ui: {
|
|
1624
|
+
linksIn: {
|
|
1625
|
+
slot: "card.footer.left",
|
|
1626
|
+
icon: "pi-download",
|
|
1627
|
+
label: "incoming links",
|
|
1628
|
+
emitWhenEmpty: false,
|
|
1629
|
+
priority: 10
|
|
1630
|
+
},
|
|
1631
|
+
linksOut: {
|
|
1632
|
+
slot: "card.footer.left",
|
|
1633
|
+
icon: "pi-upload",
|
|
1634
|
+
label: "outgoing links",
|
|
1635
|
+
emitWhenEmpty: false,
|
|
1618
1636
|
priority: 20
|
|
1619
1637
|
}
|
|
1620
1638
|
},
|
|
1639
|
+
evaluate(ctx) {
|
|
1640
|
+
const nameIndex = buildNameIndex(ctx.nodes);
|
|
1641
|
+
const perTarget = /* @__PURE__ */ new Map();
|
|
1642
|
+
const perSource = /* @__PURE__ */ new Map();
|
|
1643
|
+
for (const link2 of ctx.links) {
|
|
1644
|
+
const resolvedTarget = resolveLinkTargetToPath(link2, nameIndex);
|
|
1645
|
+
bump(perTarget, resolvedTarget, link2.kind);
|
|
1646
|
+
bump(perSource, link2.source, link2.kind);
|
|
1647
|
+
}
|
|
1648
|
+
for (const node of ctx.nodes) {
|
|
1649
|
+
emitChip(ctx, node.path, "linksIn", perTarget.get(node.path));
|
|
1650
|
+
emitChip(ctx, node.path, "linksOut", perSource.get(node.path));
|
|
1651
|
+
}
|
|
1652
|
+
return [];
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
function bump(map, key, kind) {
|
|
1656
|
+
let byKind = map.get(key);
|
|
1657
|
+
if (!byKind) {
|
|
1658
|
+
byKind = /* @__PURE__ */ new Map();
|
|
1659
|
+
map.set(key, byKind);
|
|
1660
|
+
}
|
|
1661
|
+
byKind.set(kind, (byKind.get(kind) ?? 0) + 1);
|
|
1662
|
+
}
|
|
1663
|
+
function emitChip(ctx, nodePath, contributionId, byKind) {
|
|
1664
|
+
if (!byKind) return;
|
|
1665
|
+
let total = 0;
|
|
1666
|
+
for (const n of byKind.values()) total += n;
|
|
1667
|
+
if (total === 0) return;
|
|
1668
|
+
const capped = Math.min(total, 99);
|
|
1669
|
+
const direction = contributionId === "linksIn" ? "in" : "out";
|
|
1670
|
+
ctx.emitContribution(nodePath, contributionId, {
|
|
1671
|
+
value: capped,
|
|
1672
|
+
tooltip: formatBreakdown(byKind, direction)
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
function formatBreakdown(byKind, direction) {
|
|
1676
|
+
const lines = [...byKind.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([kind, n]) => `${kind}: ${n}`);
|
|
1677
|
+
return [direction, ...lines].join("\n");
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// plugins/core/analyzers/stability/index.ts
|
|
1681
|
+
var ID14 = "stability";
|
|
1682
|
+
var EXPERIMENTAL_TOOLTIP = "Experimental: API may change";
|
|
1683
|
+
var DEPRECATED_TOOLTIP = "Deprecated: avoid in new code";
|
|
1684
|
+
var stabilityAnalyzer = {
|
|
1685
|
+
id: ID14,
|
|
1686
|
+
pluginId: "core",
|
|
1687
|
+
kind: "analyzer",
|
|
1688
|
+
version: "1.0.0",
|
|
1689
|
+
description: "Reports node lifecycle stage (`experimental`, `deprecated`) on the card.",
|
|
1690
|
+
mode: "deterministic",
|
|
1691
|
+
ui: {
|
|
1692
|
+
experimental: {
|
|
1693
|
+
slot: "card.footer.right",
|
|
1694
|
+
icon: "fa-solid fa-flask",
|
|
1695
|
+
label: "experimental",
|
|
1696
|
+
emitWhenEmpty: false,
|
|
1697
|
+
priority: 10
|
|
1698
|
+
},
|
|
1699
|
+
deprecated: {
|
|
1700
|
+
slot: "card.footer.right",
|
|
1701
|
+
icon: "pi-ban",
|
|
1702
|
+
label: "deprecated",
|
|
1703
|
+
emitWhenEmpty: false,
|
|
1704
|
+
priority: 10
|
|
1705
|
+
}
|
|
1706
|
+
},
|
|
1621
1707
|
evaluate(ctx) {
|
|
1622
1708
|
const issues = [];
|
|
1623
1709
|
for (const node of ctx.nodes) {
|
|
1624
|
-
const
|
|
1625
|
-
if (
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1710
|
+
const stability = readStability(node);
|
|
1711
|
+
if (stability === "experimental") {
|
|
1712
|
+
ctx.emitContribution(node.path, "experimental", {
|
|
1713
|
+
value: 0,
|
|
1714
|
+
tooltip: EXPERIMENTAL_TOOLTIP
|
|
1715
|
+
});
|
|
1716
|
+
issues.push({
|
|
1717
|
+
analyzerId: ID14,
|
|
1718
|
+
severity: "info",
|
|
1719
|
+
nodeIds: [node.path],
|
|
1720
|
+
message: `Node '${node.path}' is marked experimental: API may change.`,
|
|
1721
|
+
data: { stability }
|
|
1722
|
+
});
|
|
1723
|
+
} else if (stability === "deprecated") {
|
|
1724
|
+
ctx.emitContribution(node.path, "deprecated", {
|
|
1725
|
+
value: 0,
|
|
1726
|
+
tooltip: DEPRECATED_TOOLTIP,
|
|
1727
|
+
severity: "warn"
|
|
1728
|
+
});
|
|
1729
|
+
issues.push({
|
|
1730
|
+
analyzerId: ID14,
|
|
1731
|
+
severity: "warn",
|
|
1732
|
+
nodeIds: [node.path],
|
|
1733
|
+
message: `Node '${node.path}' is marked deprecated: avoid in new code.`,
|
|
1734
|
+
data: { stability }
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1640
1737
|
}
|
|
1641
1738
|
return issues;
|
|
1642
1739
|
}
|
|
1643
1740
|
};
|
|
1644
|
-
function
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1741
|
+
function readStability(node) {
|
|
1742
|
+
const fromAnn = node.sidecar?.annotations?.["stability"];
|
|
1743
|
+
if (isStability(fromAnn)) return fromAnn;
|
|
1744
|
+
const legacy = readLegacyMetadataStability(node.frontmatter);
|
|
1745
|
+
return isStability(legacy) ? legacy : null;
|
|
1746
|
+
}
|
|
1747
|
+
function readLegacyMetadataStability(fm) {
|
|
1748
|
+
if (!fm) return void 0;
|
|
1749
|
+
const meta = fm["metadata"];
|
|
1750
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
|
|
1751
|
+
return meta["stability"];
|
|
1752
|
+
}
|
|
1753
|
+
function isStability(value) {
|
|
1754
|
+
return value === "experimental" || value === "deprecated" || value === "stable";
|
|
1653
1755
|
}
|
|
1654
1756
|
|
|
1655
|
-
//
|
|
1656
|
-
var
|
|
1657
|
-
/**
|
|
1658
|
-
message: "
|
|
1757
|
+
// plugins/core/analyzers/superseded/text.ts
|
|
1758
|
+
var SUPERSEDED_TEXTS = {
|
|
1759
|
+
/** `<path> is superseded by <supersededBy>` */
|
|
1760
|
+
message: "{{path}} is superseded by {{supersededBy}}"
|
|
1659
1761
|
};
|
|
1660
1762
|
|
|
1661
|
-
//
|
|
1662
|
-
var
|
|
1663
|
-
var
|
|
1664
|
-
id:
|
|
1763
|
+
// plugins/core/analyzers/superseded/index.ts
|
|
1764
|
+
var ID15 = "superseded";
|
|
1765
|
+
var supersededAnalyzer = {
|
|
1766
|
+
id: ID15,
|
|
1665
1767
|
pluginId: "core",
|
|
1666
1768
|
kind: "analyzer",
|
|
1667
1769
|
version: "1.0.0",
|
|
1668
|
-
description: "Detects and
|
|
1669
|
-
stability: "stable",
|
|
1770
|
+
description: "Detects and marks nodes replaced by a newer one via `supersededBy`.",
|
|
1670
1771
|
mode: "deterministic",
|
|
1671
1772
|
evaluate(ctx) {
|
|
1672
|
-
const orphans = ctx.orphanSidecars;
|
|
1673
|
-
if (!orphans || orphans.length === 0) return [];
|
|
1674
1773
|
const issues = [];
|
|
1675
|
-
for (const
|
|
1676
|
-
const
|
|
1774
|
+
for (const node of ctx.nodes) {
|
|
1775
|
+
const supersededBy = pickSupersededBy(node);
|
|
1776
|
+
if (supersededBy === null) continue;
|
|
1677
1777
|
issues.push({
|
|
1678
|
-
analyzerId:
|
|
1679
|
-
severity: "
|
|
1680
|
-
nodeIds: [
|
|
1681
|
-
message: tx(
|
|
1682
|
-
|
|
1683
|
-
|
|
1778
|
+
analyzerId: ID15,
|
|
1779
|
+
severity: "info",
|
|
1780
|
+
nodeIds: [node.path],
|
|
1781
|
+
message: tx(SUPERSEDED_TEXTS.message, {
|
|
1782
|
+
path: node.path,
|
|
1783
|
+
supersededBy
|
|
1684
1784
|
}),
|
|
1685
|
-
data: {
|
|
1686
|
-
sidecarPath: orphan.relativePath,
|
|
1687
|
-
expectedMdPath: orphan.expectedMdPath
|
|
1688
|
-
}
|
|
1785
|
+
data: { supersededBy }
|
|
1689
1786
|
});
|
|
1690
1787
|
}
|
|
1691
1788
|
return issues;
|
|
1692
1789
|
}
|
|
1693
1790
|
};
|
|
1791
|
+
function pickSupersededBy(node) {
|
|
1792
|
+
const sidecar = node.sidecar;
|
|
1793
|
+
if (!sidecar || sidecar.present !== true) return null;
|
|
1794
|
+
const ann = sidecar.annotations;
|
|
1795
|
+
if (!ann || typeof ann !== "object" || Array.isArray(ann)) return null;
|
|
1796
|
+
const value = ann["supersededBy"];
|
|
1797
|
+
if (typeof value !== "string" || value.length === 0) return null;
|
|
1798
|
+
return value;
|
|
1799
|
+
}
|
|
1694
1800
|
|
|
1695
|
-
//
|
|
1696
|
-
var
|
|
1801
|
+
// plugins/core/analyzers/trigger-collision/text.ts
|
|
1802
|
+
var TRIGGER_COLLISION_TEXTS = {
|
|
1697
1803
|
/**
|
|
1698
|
-
*
|
|
1699
|
-
*
|
|
1804
|
+
* Top-level message when `analyzeTriggerBucket` accumulated exactly one
|
|
1805
|
+
* cause part. Used for the advertiser-ambiguous-only, invocation-
|
|
1806
|
+
* ambiguous-only, and cross-kind-only branches.
|
|
1700
1807
|
*/
|
|
1701
|
-
|
|
1808
|
+
messageOnePart: 'Trigger "{{normalized}}" has {{part}}.',
|
|
1809
|
+
/**
|
|
1810
|
+
* Top-level message when `analyzeTriggerBucket` accumulated two cause
|
|
1811
|
+
* parts (advertiser-ambiguous AND invocation-ambiguous fire together).
|
|
1812
|
+
* The joiner lives inside the template so future locales can adapt it
|
|
1813
|
+
* (e.g. `'; y '` in Spanish) without touching the rule code.
|
|
1814
|
+
*/
|
|
1815
|
+
messageTwoParts: 'Trigger "{{normalized}}" has {{first}}; and {{second}}.',
|
|
1816
|
+
/** `<n> nodes advertise it: <list>` part, fires on the advertiser-ambiguous branch. */
|
|
1817
|
+
partAdvertisers: "{{count}} nodes advertise it: {{paths}}",
|
|
1818
|
+
/** `<n> distinct invocation forms: <list>` part, fires on the invocation-ambiguous branch. */
|
|
1819
|
+
partInvocations: "{{count}} distinct invocation forms: {{forms}}",
|
|
1820
|
+
/** Singular cross-kind cause: `non-canonical invocation <form> against advertiser <path>`. */
|
|
1821
|
+
partNonCanonicalSingular: "non-canonical invocation {{forms}} against advertiser {{advertiser}}",
|
|
1822
|
+
/** Plural cross-kind cause: `non-canonical invocations <forms> against advertiser <path>`. */
|
|
1823
|
+
partNonCanonicalPlural: "non-canonical invocations {{forms}} against advertiser {{advertiser}}"
|
|
1702
1824
|
};
|
|
1703
1825
|
|
|
1704
|
-
//
|
|
1705
|
-
var
|
|
1706
|
-
var
|
|
1707
|
-
|
|
1826
|
+
// plugins/core/analyzers/trigger-collision/index.ts
|
|
1827
|
+
var ID16 = "trigger-collision";
|
|
1828
|
+
var ADVERTISING_KINDS = /* @__PURE__ */ new Set([
|
|
1829
|
+
"command",
|
|
1830
|
+
"skill",
|
|
1831
|
+
"agent"
|
|
1832
|
+
]);
|
|
1833
|
+
var triggerCollisionAnalyzer = {
|
|
1834
|
+
id: ID16,
|
|
1708
1835
|
pluginId: "core",
|
|
1709
1836
|
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
1837
|
mode: "deterministic",
|
|
1838
|
+
version: "1.0.0",
|
|
1839
|
+
description: "Detects and flags two or more nodes claiming the same `/command` or `@agent` name.",
|
|
1840
|
+
// Two claim-collection passes (advertisement + invocation) feeding
|
|
1841
|
+
// the bucket map. Per-bucket analysis lives in `analyzeTriggerBucket`.
|
|
1842
|
+
// eslint-disable-next-line complexity
|
|
1714
1843
|
evaluate(ctx) {
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1844
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
1845
|
+
const push = (key, claim) => {
|
|
1846
|
+
const bucket = buckets.get(key) ?? [];
|
|
1847
|
+
bucket.push(claim);
|
|
1848
|
+
buckets.set(key, bucket);
|
|
1849
|
+
};
|
|
1850
|
+
for (const node of ctx.nodes) {
|
|
1851
|
+
if (!ADVERTISING_KINDS.has(node.kind)) continue;
|
|
1852
|
+
const raw = node.frontmatter?.["name"];
|
|
1853
|
+
if (typeof raw !== "string" || raw.length === 0) continue;
|
|
1854
|
+
const normalized = `/${normalizeTrigger(raw)}`;
|
|
1855
|
+
if (normalized === "/") continue;
|
|
1856
|
+
push(normalized, {
|
|
1857
|
+
kind: "advertiser",
|
|
1858
|
+
token: node.path,
|
|
1859
|
+
nodeId: node.path,
|
|
1860
|
+
canonicalForm: `/${raw}`
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
for (const link2 of ctx.links) {
|
|
1864
|
+
const normalized = link2.trigger?.normalizedTrigger;
|
|
1865
|
+
if (!normalized) continue;
|
|
1866
|
+
push(normalized, {
|
|
1867
|
+
kind: "invocation",
|
|
1868
|
+
token: link2.target,
|
|
1869
|
+
nodeId: link2.source
|
|
1725
1870
|
});
|
|
1726
1871
|
}
|
|
1872
|
+
const issues = [];
|
|
1873
|
+
for (const [normalized, claims] of buckets) {
|
|
1874
|
+
const issue = analyzeTriggerBucket(normalized, claims);
|
|
1875
|
+
if (issue) issues.push(issue);
|
|
1876
|
+
}
|
|
1727
1877
|
return issues;
|
|
1728
1878
|
}
|
|
1729
1879
|
};
|
|
1880
|
+
function analyzeTriggerBucket(normalized, claims) {
|
|
1881
|
+
const advertiserPaths = [
|
|
1882
|
+
...new Set(claims.filter((c) => c.kind === "advertiser").map((c) => c.token))
|
|
1883
|
+
].sort();
|
|
1884
|
+
const invocationTargets = [
|
|
1885
|
+
...new Set(claims.filter((c) => c.kind === "invocation").map((c) => c.token))
|
|
1886
|
+
].sort();
|
|
1887
|
+
const advertisers = claims.filter(
|
|
1888
|
+
(c) => c.kind === "advertiser"
|
|
1889
|
+
);
|
|
1890
|
+
const advertiserAmbiguous = advertiserPaths.length >= 2;
|
|
1891
|
+
const invocationAmbiguous = invocationTargets.length >= 2;
|
|
1892
|
+
const canonicalForms = new Set(advertisers.map((a) => a.canonicalForm));
|
|
1893
|
+
const nonCanonicalInvocations = invocationTargets.filter((t) => !canonicalForms.has(t));
|
|
1894
|
+
const crossKindAmbiguous = advertiserPaths.length === 1 && nonCanonicalInvocations.length >= 1;
|
|
1895
|
+
if (!advertiserAmbiguous && !invocationAmbiguous && !crossKindAmbiguous) {
|
|
1896
|
+
return null;
|
|
1897
|
+
}
|
|
1898
|
+
const nodeIds = [...new Set(claims.map((c) => c.nodeId))].sort();
|
|
1899
|
+
const parts = [];
|
|
1900
|
+
if (advertiserAmbiguous) {
|
|
1901
|
+
parts.push(
|
|
1902
|
+
tx(TRIGGER_COLLISION_TEXTS.partAdvertisers, {
|
|
1903
|
+
count: advertiserPaths.length,
|
|
1904
|
+
paths: advertiserPaths.join(", ")
|
|
1905
|
+
})
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
if (invocationAmbiguous) {
|
|
1909
|
+
parts.push(
|
|
1910
|
+
tx(TRIGGER_COLLISION_TEXTS.partInvocations, {
|
|
1911
|
+
count: invocationTargets.length,
|
|
1912
|
+
forms: invocationTargets.join(", ")
|
|
1913
|
+
})
|
|
1914
|
+
);
|
|
1915
|
+
} else if (crossKindAmbiguous) {
|
|
1916
|
+
const template = nonCanonicalInvocations.length > 1 ? TRIGGER_COLLISION_TEXTS.partNonCanonicalPlural : TRIGGER_COLLISION_TEXTS.partNonCanonicalSingular;
|
|
1917
|
+
parts.push(
|
|
1918
|
+
tx(template, {
|
|
1919
|
+
forms: nonCanonicalInvocations.join(", "),
|
|
1920
|
+
advertiser: advertiserPaths[0]
|
|
1921
|
+
})
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
const message = parts.length === 2 ? tx(TRIGGER_COLLISION_TEXTS.messageTwoParts, {
|
|
1925
|
+
normalized,
|
|
1926
|
+
first: parts[0],
|
|
1927
|
+
second: parts[1]
|
|
1928
|
+
}) : tx(TRIGGER_COLLISION_TEXTS.messageOnePart, {
|
|
1929
|
+
normalized,
|
|
1930
|
+
part: parts[0]
|
|
1931
|
+
});
|
|
1932
|
+
return {
|
|
1933
|
+
analyzerId: ID16,
|
|
1934
|
+
severity: "error",
|
|
1935
|
+
nodeIds,
|
|
1936
|
+
message,
|
|
1937
|
+
data: {
|
|
1938
|
+
normalizedTrigger: normalized,
|
|
1939
|
+
invocationTargets,
|
|
1940
|
+
advertiserPaths
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1730
1944
|
|
|
1731
|
-
//
|
|
1945
|
+
// plugins/core/analyzers/unknown-field/index.ts
|
|
1732
1946
|
import { readFileSync } from "fs";
|
|
1733
1947
|
import { dirname, resolve as resolve2 } from "path";
|
|
1734
1948
|
import { createRequire } from "module";
|
|
@@ -1741,7 +1955,7 @@ function applyAjvFormats(ajv) {
|
|
|
1741
1955
|
addFormats(ajv);
|
|
1742
1956
|
}
|
|
1743
1957
|
|
|
1744
|
-
//
|
|
1958
|
+
// plugins/core/analyzers/unknown-field/text.ts
|
|
1745
1959
|
var UNKNOWN_FIELD_TEXTS = {
|
|
1746
1960
|
/** Key inside `annotations:` is not in the curated catalog. */
|
|
1747
1961
|
unknownAnnotationKey: "{{path}}: sidecar annotations contain unknown key '{{key}}' (not in annotations.schema.json catalog).",
|
|
@@ -1755,18 +1969,17 @@ var UNKNOWN_FIELD_TEXTS = {
|
|
|
1755
1969
|
alertTooltipMany: "This node has {{count}} unknown fields in its sidecar. Open the inspector for details."
|
|
1756
1970
|
};
|
|
1757
1971
|
|
|
1758
|
-
//
|
|
1759
|
-
var
|
|
1972
|
+
// plugins/core/analyzers/unknown-field/index.ts
|
|
1973
|
+
var ID17 = "unknown-field";
|
|
1760
1974
|
var RESERVED_ROOT_BLOCKS = /* @__PURE__ */ new Set(["identity", "annotations", "settings", "audit"]);
|
|
1761
1975
|
var unknownFieldAnalyzer = {
|
|
1762
|
-
id:
|
|
1976
|
+
id: ID17,
|
|
1763
1977
|
pluginId: "core",
|
|
1764
1978
|
kind: "analyzer",
|
|
1765
1979
|
version: "1.0.0",
|
|
1766
1980
|
description: "Detects and flags typos or unrecognized keys in sidecars (`.sm`).",
|
|
1767
|
-
stability: "stable",
|
|
1768
1981
|
mode: "deterministic",
|
|
1769
|
-
|
|
1982
|
+
ui: {
|
|
1770
1983
|
// Corner badge on the graph card; count omitted when there is a
|
|
1771
1984
|
// single unknown field (avoids a noisy "icon + 1" chip).
|
|
1772
1985
|
alert: {
|
|
@@ -1818,7 +2031,7 @@ var unknownFieldAnalyzer = {
|
|
|
1818
2031
|
for (const key of Object.keys(annotations)) {
|
|
1819
2032
|
if (!knownAnnotationKeys.has(key)) {
|
|
1820
2033
|
issues.push({
|
|
1821
|
-
analyzerId:
|
|
2034
|
+
analyzerId: ID17,
|
|
1822
2035
|
severity: "warn",
|
|
1823
2036
|
nodeIds: [node.path],
|
|
1824
2037
|
message: tx(UNKNOWN_FIELD_TEXTS.unknownAnnotationKey, {
|
|
@@ -1845,7 +2058,7 @@ var unknownFieldAnalyzer = {
|
|
|
1845
2058
|
if (validator(value)) continue;
|
|
1846
2059
|
const errors = (validator.errors ?? []).map((e) => `${e.instancePath || "(root)"} ${e.message ?? e.keyword}`).join("; ");
|
|
1847
2060
|
issues.push({
|
|
1848
|
-
analyzerId:
|
|
2061
|
+
analyzerId: ID17,
|
|
1849
2062
|
severity: "warn",
|
|
1850
2063
|
nodeIds: [node.path],
|
|
1851
2064
|
message: tx(UNKNOWN_FIELD_TEXTS.pluginNamespaceInvalid, {
|
|
@@ -1861,7 +2074,7 @@ var unknownFieldAnalyzer = {
|
|
|
1861
2074
|
continue;
|
|
1862
2075
|
}
|
|
1863
2076
|
issues.push({
|
|
1864
|
-
analyzerId:
|
|
2077
|
+
analyzerId: ID17,
|
|
1865
2078
|
severity: "warn",
|
|
1866
2079
|
nodeIds: [node.path],
|
|
1867
2080
|
message: tx(UNKNOWN_FIELD_TEXTS.unknownRootKey, {
|
|
@@ -1922,181 +2135,15 @@ function indexNamespacedContributions(contributions) {
|
|
|
1922
2135
|
function indexRootContributions(contributions) {
|
|
1923
2136
|
const out = /* @__PURE__ */ new Set();
|
|
1924
2137
|
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
|
-
});
|
|
2138
|
+
if (entry.location === "root") out.add(entry.key);
|
|
2098
2139
|
}
|
|
2099
|
-
|
|
2140
|
+
return out;
|
|
2141
|
+
}
|
|
2142
|
+
function collectPluginIds(contributions) {
|
|
2143
|
+
const out = /* @__PURE__ */ new Set();
|
|
2144
|
+
for (const entry of contributions) out.add(entry.pluginId);
|
|
2145
|
+
return out;
|
|
2146
|
+
}
|
|
2100
2147
|
|
|
2101
2148
|
// kernel/adapters/schema-validators.ts
|
|
2102
2149
|
import { readFileSync as readFileSync2 } from "fs";
|
|
@@ -2138,6 +2185,7 @@ var SCHEMA_FILES = {
|
|
|
2138
2185
|
"conformance-case": "schemas/conformance-case.schema.json",
|
|
2139
2186
|
"history-stats": "schemas/history-stats.schema.json",
|
|
2140
2187
|
"extension-provider": "schemas/extensions/provider.schema.json",
|
|
2188
|
+
"extension-provider-kind": "schemas/extensions/provider-kind.schema.json",
|
|
2141
2189
|
"extension-extractor": "schemas/extensions/extractor.schema.json",
|
|
2142
2190
|
"extension-analyzer": "schemas/extensions/analyzer.schema.json",
|
|
2143
2191
|
"extension-action": "schemas/extensions/action.schema.json",
|
|
@@ -2304,7 +2352,7 @@ function existsSyncSafe(path) {
|
|
|
2304
2352
|
}
|
|
2305
2353
|
}
|
|
2306
2354
|
|
|
2307
|
-
//
|
|
2355
|
+
// plugins/core/analyzers/validate-all/text.ts
|
|
2308
2356
|
var VALIDATE_ALL_TEXTS = {
|
|
2309
2357
|
/** `Node <path> failed schema validation: <errors>` */
|
|
2310
2358
|
nodeFailure: "Node {{path}} failed schema validation: {{errors}}",
|
|
@@ -2318,17 +2366,16 @@ var VALIDATE_ALL_TEXTS = {
|
|
|
2318
2366
|
alertTooltipMany: "{{count}} schema validation issues on this node."
|
|
2319
2367
|
};
|
|
2320
2368
|
|
|
2321
|
-
//
|
|
2322
|
-
var
|
|
2369
|
+
// plugins/core/analyzers/validate-all/index.ts
|
|
2370
|
+
var ID18 = "validate-all";
|
|
2323
2371
|
var validateAllAnalyzer = {
|
|
2324
|
-
id:
|
|
2372
|
+
id: ID18,
|
|
2325
2373
|
pluginId: "core",
|
|
2326
2374
|
kind: "analyzer",
|
|
2327
2375
|
version: "1.0.0",
|
|
2328
2376
|
description: "Detects and flags nodes or links violating the project schemas.",
|
|
2329
|
-
stability: "stable",
|
|
2330
2377
|
mode: "deterministic",
|
|
2331
|
-
|
|
2378
|
+
ui: {
|
|
2332
2379
|
// Corner badge on the graph card; surfaces when the node body /
|
|
2333
2380
|
// frontmatter fails schema validation (parse error, missing
|
|
2334
2381
|
// `name`/`description`, malformed YAML, etc.). Same visual
|
|
@@ -2385,7 +2432,7 @@ function collectNodeFindings(v, node, out) {
|
|
|
2385
2432
|
const result = v.validate("node", toNodeForSchema(node));
|
|
2386
2433
|
if (result.ok) return;
|
|
2387
2434
|
out.push({
|
|
2388
|
-
analyzerId:
|
|
2435
|
+
analyzerId: ID18,
|
|
2389
2436
|
severity: "error",
|
|
2390
2437
|
nodeIds: [node.path],
|
|
2391
2438
|
message: tx(VALIDATE_ALL_TEXTS.nodeFailure, {
|
|
@@ -2404,7 +2451,7 @@ function collectFrontmatterBaseFindings(node, out) {
|
|
|
2404
2451
|
if (isMissingStringField(fm, "description")) missing.push("description");
|
|
2405
2452
|
if (missing.length === 0) return;
|
|
2406
2453
|
out.push({
|
|
2407
|
-
analyzerId:
|
|
2454
|
+
analyzerId: ID18,
|
|
2408
2455
|
// `warn` (not `error`) so the default `sm scan` exit code stays
|
|
2409
2456
|
// 0 even when nodes are missing frontmatter base fields. Strict
|
|
2410
2457
|
// mode (`sm scan --strict`) still escalates to exit 1. Matches
|
|
@@ -2426,7 +2473,7 @@ function collectLinkFindings(v, link2, out) {
|
|
|
2426
2473
|
const result = v.validate("link", toLinkForSchema(link2));
|
|
2427
2474
|
if (result.ok) return;
|
|
2428
2475
|
out.push({
|
|
2429
|
-
analyzerId:
|
|
2476
|
+
analyzerId: ID18,
|
|
2430
2477
|
severity: "error",
|
|
2431
2478
|
nodeIds: [link2.source],
|
|
2432
2479
|
message: tx(VALIDATE_ALL_TEXTS.linkFailure, {
|
|
@@ -2466,120 +2513,154 @@ function toLinkForSchema(link2) {
|
|
|
2466
2513
|
};
|
|
2467
2514
|
}
|
|
2468
2515
|
|
|
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
|
-
}
|
|
2516
|
+
// kernel/util/safe-text.ts
|
|
2517
|
+
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;
|
|
2518
|
+
var C0_CONTROL_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
|
|
2519
|
+
function sanitizeForTerminal(text) {
|
|
2520
|
+
return text.replace(ANSI_ESCAPE_RE, "").replace(C0_CONTROL_RE, "");
|
|
2483
2521
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2522
|
+
|
|
2523
|
+
// plugins/core/formatters/ascii/text.ts
|
|
2524
|
+
var ASCII_FORMATTER_TEXTS = {
|
|
2525
|
+
/** Header line: `skill-map graph: N nodes, M links, K issues`. */
|
|
2526
|
+
header: "skill-map graph: {{nodes}} nodes, {{links}} links, {{issues}} issues",
|
|
2527
|
+
/** Per-node-kind section header: `## <kind> (<count>)`. */
|
|
2528
|
+
kindSectionHeader: "## {{kind}} ({{count}})",
|
|
2529
|
+
/** Plain node bullet: `- <path>`. */
|
|
2530
|
+
nodeBullet: "- {{path}}",
|
|
2531
|
+
/** Node bullet with title suffix: `- <path>: "<title>"`. */
|
|
2532
|
+
nodeBulletWithTitle: '- {{path}}: "{{title}}"',
|
|
2533
|
+
/** `## links (<count>)` section header. */
|
|
2534
|
+
linksSectionHeader: "## links ({{count}})",
|
|
2535
|
+
/** Link bullet: `- <source> --<kind>--> <target> [<confidence>]`. */
|
|
2536
|
+
linkBullet: "- {{source}} --{{kind}}--> {{target}} [{{confidence}}]",
|
|
2537
|
+
/** `## issues (<count>)` section header. */
|
|
2538
|
+
issuesSectionHeader: "## issues ({{count}})",
|
|
2539
|
+
/** Issue bullet: `- [<severity>] <analyzerId>: <message>`. */
|
|
2540
|
+
issueBullet: "- [{{severity}}] {{analyzerId}}: {{message}}"
|
|
2541
|
+
};
|
|
2542
|
+
|
|
2543
|
+
// plugins/core/formatters/ascii/index.ts
|
|
2544
|
+
var ID19 = "ascii";
|
|
2545
|
+
var KIND_ORDER = ["agent", "command", "skill", "markdown"];
|
|
2546
|
+
var asciiFormatter = {
|
|
2547
|
+
id: ID19,
|
|
2548
|
+
pluginId: "core",
|
|
2549
|
+
kind: "formatter",
|
|
2550
|
+
formatId: ID19,
|
|
2551
|
+
version: "1.0.0",
|
|
2552
|
+
description: "Renders the scan as plain text, grouped by kind, arrows, and issues. Used by `sm scan --format=ascii`.",
|
|
2553
|
+
// ASCII tree formatter, header + per-kind sections + per-issue
|
|
2554
|
+
// section. Each section iterates and renders; splitting per section
|
|
2555
|
+
// would multiply the for-loop boilerplate.
|
|
2556
|
+
// eslint-disable-next-line complexity
|
|
2557
|
+
format(ctx) {
|
|
2558
|
+
const out = [];
|
|
2559
|
+
out.push(
|
|
2560
|
+
tx(ASCII_FORMATTER_TEXTS.header, {
|
|
2561
|
+
nodes: ctx.nodes.length,
|
|
2562
|
+
links: ctx.links.length,
|
|
2563
|
+
issues: ctx.issues.length
|
|
2564
|
+
}),
|
|
2565
|
+
""
|
|
2566
|
+
);
|
|
2567
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
2568
|
+
for (const node of ctx.nodes) {
|
|
2569
|
+
if (!byKind.has(node.kind)) byKind.set(node.kind, []);
|
|
2570
|
+
byKind.get(node.kind).push(node);
|
|
2571
|
+
}
|
|
2572
|
+
const renderedKinds = /* @__PURE__ */ new Set();
|
|
2573
|
+
for (const kind of KIND_ORDER) {
|
|
2574
|
+
const group = byKind.get(kind);
|
|
2575
|
+
if (!group || group.length === 0) continue;
|
|
2576
|
+
renderSection(out, kind, group);
|
|
2577
|
+
renderedKinds.add(kind);
|
|
2578
|
+
}
|
|
2579
|
+
const extraKinds = [...byKind.keys()].filter((k) => !renderedKinds.has(k)).sort();
|
|
2580
|
+
for (const kind of extraKinds) {
|
|
2581
|
+
const group = byKind.get(kind);
|
|
2582
|
+
if (!group || group.length === 0) continue;
|
|
2583
|
+
renderSection(out, kind, group);
|
|
2584
|
+
}
|
|
2585
|
+
if (ctx.links.length > 0) {
|
|
2586
|
+
out.push(tx(ASCII_FORMATTER_TEXTS.linksSectionHeader, { count: ctx.links.length }));
|
|
2587
|
+
const sorted = [...ctx.links].sort((a, b) => {
|
|
2588
|
+
const aKey = `${a.source}\0${a.kind}\0${a.target}`;
|
|
2589
|
+
const bKey = `${b.source}\0${b.kind}\0${b.target}`;
|
|
2590
|
+
return aKey.localeCompare(bKey);
|
|
2591
|
+
});
|
|
2592
|
+
for (const link2 of sorted) {
|
|
2593
|
+
out.push(
|
|
2594
|
+
tx(ASCII_FORMATTER_TEXTS.linkBullet, {
|
|
2595
|
+
source: sanitizeForTerminal(link2.source),
|
|
2596
|
+
kind: sanitizeForTerminal(link2.kind),
|
|
2597
|
+
target: sanitizeForTerminal(link2.target),
|
|
2598
|
+
confidence: link2.confidence
|
|
2599
|
+
})
|
|
2600
|
+
);
|
|
2601
|
+
}
|
|
2602
|
+
out.push("");
|
|
2603
|
+
}
|
|
2604
|
+
if (ctx.issues.length > 0) {
|
|
2605
|
+
out.push(tx(ASCII_FORMATTER_TEXTS.issuesSectionHeader, { count: ctx.issues.length }));
|
|
2606
|
+
for (const issue of ctx.issues) {
|
|
2607
|
+
out.push(
|
|
2608
|
+
tx(ASCII_FORMATTER_TEXTS.issueBullet, {
|
|
2609
|
+
severity: issue.severity,
|
|
2610
|
+
analyzerId: sanitizeForTerminal(issue.analyzerId),
|
|
2611
|
+
message: sanitizeForTerminal(issue.message)
|
|
2612
|
+
})
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
out.push("");
|
|
2616
|
+
}
|
|
2617
|
+
return out.join("\n");
|
|
2491
2618
|
}
|
|
2619
|
+
};
|
|
2620
|
+
function pickTitle(node) {
|
|
2621
|
+
const name = node.frontmatter?.["name"];
|
|
2622
|
+
return typeof name === "string" && name.length > 0 ? name : null;
|
|
2492
2623
|
}
|
|
2493
|
-
function
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2624
|
+
function renderSection(out, kind, group) {
|
|
2625
|
+
const sorted = [...group].sort((a, b) => a.path.localeCompare(b.path));
|
|
2626
|
+
out.push(
|
|
2627
|
+
tx(ASCII_FORMATTER_TEXTS.kindSectionHeader, {
|
|
2628
|
+
kind: sanitizeForTerminal(kind),
|
|
2629
|
+
count: sorted.length
|
|
2630
|
+
})
|
|
2631
|
+
);
|
|
2632
|
+
for (const node of sorted) {
|
|
2633
|
+
const title = pickTitle(node);
|
|
2634
|
+
out.push(
|
|
2635
|
+
title ? tx(ASCII_FORMATTER_TEXTS.nodeBulletWithTitle, {
|
|
2636
|
+
path: sanitizeForTerminal(node.path),
|
|
2637
|
+
title: sanitizeForTerminal(title)
|
|
2638
|
+
}) : tx(ASCII_FORMATTER_TEXTS.nodeBullet, { path: sanitizeForTerminal(node.path) })
|
|
2639
|
+
);
|
|
2504
2640
|
}
|
|
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;
|
|
2641
|
+
out.push("");
|
|
2515
2642
|
}
|
|
2516
2643
|
|
|
2517
|
-
//
|
|
2518
|
-
var ID20 = "
|
|
2519
|
-
var
|
|
2644
|
+
// plugins/core/formatters/json/index.ts
|
|
2645
|
+
var ID20 = "json";
|
|
2646
|
+
var jsonFormatter = {
|
|
2520
2647
|
id: ID20,
|
|
2521
2648
|
pluginId: "core",
|
|
2522
|
-
kind: "
|
|
2649
|
+
kind: "formatter",
|
|
2523
2650
|
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));
|
|
2651
|
+
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`.",
|
|
2652
|
+
formatId: ID20,
|
|
2653
|
+
format(ctx) {
|
|
2654
|
+
if (ctx.scanResult !== void 0) {
|
|
2655
|
+
return JSON.stringify(ctx.scanResult);
|
|
2555
2656
|
}
|
|
2556
|
-
return
|
|
2657
|
+
return JSON.stringify({
|
|
2658
|
+
nodes: ctx.nodes,
|
|
2659
|
+
links: ctx.links,
|
|
2660
|
+
issues: ctx.issues
|
|
2661
|
+
});
|
|
2557
2662
|
}
|
|
2558
2663
|
};
|
|
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
2664
|
|
|
2584
2665
|
// kernel/sidecar/parse.ts
|
|
2585
2666
|
import { existsSync, readFileSync as readFileSync3 } from "fs";
|
|
@@ -2706,7 +2787,7 @@ function resolveSpecRoot2() {
|
|
|
2706
2787
|
}
|
|
2707
2788
|
}
|
|
2708
2789
|
|
|
2709
|
-
//
|
|
2790
|
+
// plugins/core/actions/bump/index.ts
|
|
2710
2791
|
var ID21 = "bump";
|
|
2711
2792
|
var PLUGIN_ID = "core";
|
|
2712
2793
|
var bumpAction = {
|
|
@@ -2715,9 +2796,7 @@ var bumpAction = {
|
|
|
2715
2796
|
kind: "action",
|
|
2716
2797
|
version: "1.0.0",
|
|
2717
2798
|
description: "Marks a node as updated: bumps version, refreshes sidecar hashes, records the timestamp.",
|
|
2718
|
-
stability: "stable",
|
|
2719
2799
|
mode: "deterministic",
|
|
2720
|
-
reportSchemaRef: "https://skill-map.dev/spec/v0/bump-report.schema.json",
|
|
2721
2800
|
// The runtime contract uses generic <TInput, TReport>; bump narrows
|
|
2722
2801
|
// both. The cast is the standard pattern for built-ins that want
|
|
2723
2802
|
// typed local I/O while staying compatible with the open generic.
|
|
@@ -2770,7 +2849,7 @@ function pickCurrentVersion(overlay) {
|
|
|
2770
2849
|
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
2771
2850
|
}
|
|
2772
2851
|
|
|
2773
|
-
//
|
|
2852
|
+
// plugins/core/actions/mark-superseded/index.ts
|
|
2774
2853
|
var ID22 = "mark-superseded";
|
|
2775
2854
|
var PLUGIN_ID2 = "core";
|
|
2776
2855
|
var markSupersededAction = {
|
|
@@ -2779,9 +2858,7 @@ var markSupersededAction = {
|
|
|
2779
2858
|
kind: "action",
|
|
2780
2859
|
version: "0.0.0",
|
|
2781
2860
|
description: "Declares the current node as superseded by another (writes `supersededBy` to the sidecar). Paired with the `core/superseded` analyzer.",
|
|
2782
|
-
stability: "experimental",
|
|
2783
2861
|
mode: "deterministic",
|
|
2784
|
-
reportSchemaRef: "https://skill-map.dev/spec/v0/report-base-deterministic.schema.json",
|
|
2785
2862
|
invoke(_input, _ctx) {
|
|
2786
2863
|
const report = { ok: true, noop: true };
|
|
2787
2864
|
return { report };
|
|
@@ -2886,7 +2963,7 @@ var UPDATE_CHECK_TEXTS = {
|
|
|
2886
2963
|
// package.json
|
|
2887
2964
|
var package_default = {
|
|
2888
2965
|
name: "@skill-map/cli",
|
|
2889
|
-
version: "0.
|
|
2966
|
+
version: "0.30.0",
|
|
2890
2967
|
description: "skill-map reference implementation \u2014 kernel + CLI + adapters.",
|
|
2891
2968
|
license: "MIT",
|
|
2892
2969
|
type: "module",
|
|
@@ -2939,16 +3016,19 @@ var package_default = {
|
|
|
2939
3016
|
"lint:fix": "eslint . --fix",
|
|
2940
3017
|
reference: "node scripts/build-reference.js",
|
|
2941
3018
|
"reference:check": "node scripts/build-reference.js --check",
|
|
3019
|
+
"build-built-ins": "node ../scripts/generate-built-ins.js",
|
|
3020
|
+
"built-ins:check": "node ../scripts/generate-built-ins.js --check",
|
|
3021
|
+
prebuild: "pnpm build-built-ins",
|
|
2942
3022
|
validate: "pnpm validate:compile && pnpm validate:test",
|
|
2943
|
-
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm reference:check",
|
|
3023
|
+
"validate:compile": "pnpm typecheck && pnpm lint && pnpm build && pnpm reference:check && pnpm built-ins:check",
|
|
2944
3024
|
"validate:test": "pnpm test:ci",
|
|
2945
3025
|
pretest: "tsup",
|
|
2946
3026
|
"pretest:coverage": "tsup",
|
|
2947
3027
|
"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 '
|
|
3028
|
+
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'",
|
|
3029
|
+
"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'",
|
|
3030
|
+
"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'",
|
|
3031
|
+
"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
3032
|
clean: "rm -rf dist coverage"
|
|
2953
3033
|
},
|
|
2954
3034
|
dependencies: {
|
|
@@ -3237,15 +3317,13 @@ ${footer}
|
|
|
3237
3317
|
`);
|
|
3238
3318
|
}
|
|
3239
3319
|
|
|
3240
|
-
//
|
|
3320
|
+
// plugins/core/hooks/update-check/index.ts
|
|
3241
3321
|
var updateCheckHook = {
|
|
3242
3322
|
id: "update-check",
|
|
3243
3323
|
pluginId: "core",
|
|
3244
3324
|
kind: "hook",
|
|
3245
3325
|
version: "1.0.0",
|
|
3246
3326
|
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
3327
|
triggers: ["boot"],
|
|
3250
3328
|
async on(ctx) {
|
|
3251
3329
|
const payload = ctx.event.data ?? {};
|
|
@@ -3259,14 +3337,41 @@ var updateCheckHook = {
|
|
|
3259
3337
|
}
|
|
3260
3338
|
};
|
|
3261
3339
|
|
|
3262
|
-
//
|
|
3340
|
+
// plugins/built-ins.ts
|
|
3341
|
+
var claudeProvider2 = { ...claudeProvider, pluginId: "claude" };
|
|
3342
|
+
var geminiProvider2 = { ...geminiProvider, pluginId: "gemini" };
|
|
3343
|
+
var agentSkillsProvider2 = { ...agentSkillsProvider, pluginId: "agent-skills" };
|
|
3344
|
+
var coreMarkdownProvider2 = { ...coreMarkdownProvider, pluginId: "core" };
|
|
3345
|
+
var annotationsExtractor2 = { ...annotationsExtractor, pluginId: "core" };
|
|
3346
|
+
var atDirectiveExtractor2 = { ...atDirectiveExtractor, pluginId: "core" };
|
|
3347
|
+
var externalUrlCounterExtractor2 = { ...externalUrlCounterExtractor, pluginId: "core" };
|
|
3348
|
+
var markdownLinkExtractor2 = { ...markdownLinkExtractor, pluginId: "core" };
|
|
3349
|
+
var slashExtractor2 = { ...slashExtractor, pluginId: "core" };
|
|
3350
|
+
var toolsCountExtractor2 = { ...toolsCountExtractor, pluginId: "core" };
|
|
3351
|
+
var annotationOrphanAnalyzer2 = { ...annotationOrphanAnalyzer, pluginId: "core" };
|
|
3352
|
+
var annotationStaleAnalyzer2 = { ...annotationStaleAnalyzer, pluginId: "core" };
|
|
3353
|
+
var brokenRefAnalyzer2 = { ...brokenRefAnalyzer, pluginId: "core" };
|
|
3354
|
+
var contributionOrphanAnalyzer2 = { ...contributionOrphanAnalyzer, pluginId: "core" };
|
|
3355
|
+
var jobOrphanFileAnalyzer2 = { ...jobOrphanFileAnalyzer, pluginId: "core" };
|
|
3356
|
+
var linkConflictAnalyzer2 = { ...linkConflictAnalyzer, pluginId: "core" };
|
|
3357
|
+
var linkCountsAnalyzer2 = { ...linkCountsAnalyzer, pluginId: "core" };
|
|
3358
|
+
var stabilityAnalyzer2 = { ...stabilityAnalyzer, pluginId: "core" };
|
|
3359
|
+
var supersededAnalyzer2 = { ...supersededAnalyzer, pluginId: "core" };
|
|
3360
|
+
var triggerCollisionAnalyzer2 = { ...triggerCollisionAnalyzer, pluginId: "core" };
|
|
3361
|
+
var unknownFieldAnalyzer2 = { ...unknownFieldAnalyzer, pluginId: "core" };
|
|
3362
|
+
var validateAllAnalyzer2 = { ...validateAllAnalyzer, pluginId: "core" };
|
|
3363
|
+
var asciiFormatter2 = { ...asciiFormatter, pluginId: "core" };
|
|
3364
|
+
var jsonFormatter2 = { ...jsonFormatter, pluginId: "core" };
|
|
3365
|
+
var bumpAction2 = { ...bumpAction, pluginId: "core" };
|
|
3366
|
+
var markSupersededAction2 = { ...markSupersededAction, pluginId: "core" };
|
|
3367
|
+
var updateCheckHook2 = { ...updateCheckHook, pluginId: "core" };
|
|
3263
3368
|
var builtInBundles = [
|
|
3264
3369
|
{
|
|
3265
3370
|
id: "claude",
|
|
3266
3371
|
granularity: "bundle",
|
|
3267
3372
|
description: "Claude Code platform integration. Classifies files under `.claude/{agents,commands,skills}` and parses Claude-flavored frontmatter.",
|
|
3268
3373
|
extensions: [
|
|
3269
|
-
|
|
3374
|
+
claudeProvider2
|
|
3270
3375
|
]
|
|
3271
3376
|
},
|
|
3272
3377
|
{
|
|
@@ -3274,7 +3379,7 @@ var builtInBundles = [
|
|
|
3274
3379
|
granularity: "bundle",
|
|
3275
3380
|
description: "Gemini CLI platform integration. Classifies files under `.gemini/{agents,skills}` and parses Gemini-flavored frontmatter.",
|
|
3276
3381
|
extensions: [
|
|
3277
|
-
|
|
3382
|
+
geminiProvider2
|
|
3278
3383
|
]
|
|
3279
3384
|
},
|
|
3280
3385
|
{
|
|
@@ -3282,7 +3387,7 @@ var builtInBundles = [
|
|
|
3282
3387
|
granularity: "bundle",
|
|
3283
3388
|
description: "Agent Skills open standard. Vendor-neutral path `.agents/skills/<name>/SKILL.md` (Anthropic, OpenAI, Google). See agentskills.io.",
|
|
3284
3389
|
extensions: [
|
|
3285
|
-
|
|
3390
|
+
agentSkillsProvider2
|
|
3286
3391
|
]
|
|
3287
3392
|
},
|
|
3288
3393
|
{
|
|
@@ -3290,39 +3395,30 @@ var builtInBundles = [
|
|
|
3290
3395
|
granularity: "extension",
|
|
3291
3396
|
description: "Core extensions shared across providers: extractors, analyzers, formatters, the bump action, and the universal `.md` fallback Provider.",
|
|
3292
3397
|
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
|
|
3398
|
+
coreMarkdownProvider2,
|
|
3399
|
+
annotationsExtractor2,
|
|
3400
|
+
atDirectiveExtractor2,
|
|
3401
|
+
externalUrlCounterExtractor2,
|
|
3402
|
+
markdownLinkExtractor2,
|
|
3403
|
+
slashExtractor2,
|
|
3404
|
+
toolsCountExtractor2,
|
|
3405
|
+
annotationOrphanAnalyzer2,
|
|
3406
|
+
annotationStaleAnalyzer2,
|
|
3407
|
+
brokenRefAnalyzer2,
|
|
3408
|
+
contributionOrphanAnalyzer2,
|
|
3409
|
+
jobOrphanFileAnalyzer2,
|
|
3410
|
+
linkConflictAnalyzer2,
|
|
3411
|
+
linkCountsAnalyzer2,
|
|
3412
|
+
stabilityAnalyzer2,
|
|
3413
|
+
supersededAnalyzer2,
|
|
3414
|
+
triggerCollisionAnalyzer2,
|
|
3415
|
+
unknownFieldAnalyzer2,
|
|
3416
|
+
validateAllAnalyzer2,
|
|
3417
|
+
asciiFormatter2,
|
|
3418
|
+
jsonFormatter2,
|
|
3419
|
+
bumpAction2,
|
|
3420
|
+
markSupersededAction2,
|
|
3421
|
+
updateCheckHook2
|
|
3326
3422
|
]
|
|
3327
3423
|
}
|
|
3328
3424
|
];
|
|
@@ -3331,8 +3427,8 @@ function builtIns() {
|
|
|
3331
3427
|
providers: [],
|
|
3332
3428
|
extractors: [],
|
|
3333
3429
|
analyzers: [],
|
|
3334
|
-
actions: [],
|
|
3335
3430
|
formatters: [],
|
|
3431
|
+
actions: [],
|
|
3336
3432
|
hooks: []
|
|
3337
3433
|
};
|
|
3338
3434
|
for (const bundle of builtInBundles) {
|
|
@@ -3356,8 +3452,8 @@ function bucketBuiltIn(ext, out) {
|
|
|
3356
3452
|
provider: out.providers,
|
|
3357
3453
|
extractor: out.extractors,
|
|
3358
3454
|
analyzer: out.analyzers,
|
|
3359
|
-
action: out.actions,
|
|
3360
3455
|
formatter: out.formatters,
|
|
3456
|
+
action: out.actions,
|
|
3361
3457
|
hook: out.hooks
|
|
3362
3458
|
});
|
|
3363
3459
|
}
|
|
@@ -3366,12 +3462,9 @@ function toExtensionRow(x) {
|
|
|
3366
3462
|
id: x.id,
|
|
3367
3463
|
pluginId: x.pluginId,
|
|
3368
3464
|
kind: x.kind,
|
|
3369
|
-
version: x.version
|
|
3465
|
+
version: x.version,
|
|
3466
|
+
description: x.description ?? ""
|
|
3370
3467
|
};
|
|
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
3468
|
return row;
|
|
3376
3469
|
}
|
|
3377
3470
|
|
|
@@ -4131,14 +4224,14 @@ function readJsonObjectOrEmpty(path) {
|
|
|
4131
4224
|
}
|
|
4132
4225
|
return {};
|
|
4133
4226
|
}
|
|
4134
|
-
function writeFileAtomicExclusive(path, content) {
|
|
4227
|
+
function writeFileAtomicExclusive(path, content, mode = 384) {
|
|
4135
4228
|
const tmp = `${path}.tmp.${process.pid}.${randomBytes(8).toString("hex")}`;
|
|
4136
4229
|
let fd = null;
|
|
4137
4230
|
try {
|
|
4138
4231
|
fd = openSync(
|
|
4139
4232
|
tmp,
|
|
4140
4233
|
fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_NOFOLLOW,
|
|
4141
|
-
|
|
4234
|
+
mode
|
|
4142
4235
|
);
|
|
4143
4236
|
writeSync(fd, content);
|
|
4144
4237
|
closeSync(fd);
|
|
@@ -7124,7 +7217,8 @@ function invokeBumpFor(node, absPath, force) {
|
|
|
7124
7217
|
node,
|
|
7125
7218
|
nodeAbsolutePath: absPath,
|
|
7126
7219
|
invoker: "cli",
|
|
7127
|
-
now: () => /* @__PURE__ */ new Date()
|
|
7220
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
7221
|
+
settings: {}
|
|
7128
7222
|
});
|
|
7129
7223
|
}
|
|
7130
7224
|
|
|
@@ -7644,8 +7738,8 @@ var PLUGIN_LOADER_TEXTS = {
|
|
|
7644
7738
|
|
|
7645
7739
|
// kernel/adapters/plugin-loader/index.ts
|
|
7646
7740
|
import { createRequire as createRequire5 } from "module";
|
|
7647
|
-
import { existsSync as
|
|
7648
|
-
import { join as
|
|
7741
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
|
|
7742
|
+
import { join as join8, resolve as resolve16 } from "path";
|
|
7649
7743
|
import { pathToFileURL } from "url";
|
|
7650
7744
|
import semver from "semver";
|
|
7651
7745
|
|
|
@@ -7682,7 +7776,7 @@ function applyIdCollisions(plugins) {
|
|
|
7682
7776
|
const buckets = /* @__PURE__ */ new Map();
|
|
7683
7777
|
for (const p of plugins) {
|
|
7684
7778
|
if (!p.manifest) continue;
|
|
7685
|
-
const id = p.
|
|
7779
|
+
const id = p.id;
|
|
7686
7780
|
const bucket = buckets.get(id);
|
|
7687
7781
|
if (bucket) bucket.push(p);
|
|
7688
7782
|
else buckets.set(id, [p]);
|
|
@@ -7733,39 +7827,22 @@ function extractDefault(mod) {
|
|
|
7733
7827
|
if (!isRecord(mod)) return mod;
|
|
7734
7828
|
return "default" in mod ? mod["default"] : mod;
|
|
7735
7829
|
}
|
|
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) {
|
|
7830
|
+
var LOADER_INJECTED_KEYS = /* @__PURE__ */ new Set(["pluginId", "id", "kind", "kinds", "formatId"]);
|
|
7831
|
+
function stripFunctionsAndPluginId(input) {
|
|
7832
|
+
if (!isRecord(input)) return input;
|
|
7751
7833
|
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;
|
|
7834
|
+
for (const [k, v] of Object.entries(input)) {
|
|
7835
|
+
if (typeof v === "function") continue;
|
|
7836
|
+
if (LOADER_INJECTED_KEYS.has(k)) continue;
|
|
7837
|
+
out[k] = v;
|
|
7764
7838
|
}
|
|
7765
7839
|
return out;
|
|
7766
7840
|
}
|
|
7767
7841
|
|
|
7768
7842
|
// kernel/adapters/plugin-loader/validation.ts
|
|
7843
|
+
import * as nodeFs from "fs";
|
|
7844
|
+
import { existsSync as existsSync11 } from "fs";
|
|
7845
|
+
import { dirname as dirname9, join as join7 } from "path";
|
|
7769
7846
|
import { Ajv2020 as Ajv20205 } from "ajv/dist/2020.js";
|
|
7770
7847
|
|
|
7771
7848
|
// kernel/extensions/hook.ts
|
|
@@ -7793,76 +7870,73 @@ var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
|
7793
7870
|
]);
|
|
7794
7871
|
var KNOWN_KINDS_LIST = [...KNOWN_KINDS].join(" / ");
|
|
7795
7872
|
var HOOKABLE_TRIGGERS_LIST = HOOK_TRIGGERS.join(", ");
|
|
7796
|
-
function validateAnnotationContributions(pluginPath, manifest, relEntry, manifestView) {
|
|
7873
|
+
function validateAnnotationContributions(pluginPath, pluginId, manifest, relEntry, manifestView) {
|
|
7797
7874
|
if (!isRecord(manifestView)) return null;
|
|
7798
|
-
const raw = manifestView["
|
|
7875
|
+
const raw = manifestView["annotation"];
|
|
7799
7876
|
if (raw === void 0) return null;
|
|
7800
7877
|
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
|
-
}
|
|
7878
|
+
const location = raw["location"] ?? "namespaced";
|
|
7879
|
+
const ownership = raw["ownership"] ?? "shared";
|
|
7880
|
+
if (location === "root" && ownership !== "exclusive") {
|
|
7881
|
+
return {
|
|
7882
|
+
...fail(
|
|
7883
|
+
pluginPath,
|
|
7884
|
+
pluginId,
|
|
7885
|
+
"invalid-manifest",
|
|
7886
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestRootSharedAnnotation, {
|
|
7887
|
+
relEntry,
|
|
7888
|
+
key: "<annotation>",
|
|
7889
|
+
ownership
|
|
7890
|
+
})
|
|
7891
|
+
),
|
|
7892
|
+
manifest
|
|
7893
|
+
};
|
|
7894
|
+
}
|
|
7895
|
+
const schema = raw["schema"];
|
|
7896
|
+
if (!isRecord(schema)) {
|
|
7897
|
+
return {
|
|
7898
|
+
...fail(
|
|
7899
|
+
pluginPath,
|
|
7900
|
+
pluginId,
|
|
7901
|
+
"invalid-manifest",
|
|
7902
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestAnnotationSchemaCompile, {
|
|
7903
|
+
relEntry,
|
|
7904
|
+
key: "<annotation>",
|
|
7905
|
+
errDescription: "schema must be an object literal"
|
|
7906
|
+
})
|
|
7907
|
+
),
|
|
7908
|
+
manifest
|
|
7909
|
+
};
|
|
7910
|
+
}
|
|
7911
|
+
try {
|
|
7912
|
+
const ajv = new Ajv20205({ strict: false, allErrors: true, allowUnionTypes: true });
|
|
7913
|
+
applyAjvFormats(ajv);
|
|
7914
|
+
ajv.compile(schema);
|
|
7915
|
+
} catch (err) {
|
|
7916
|
+
return {
|
|
7917
|
+
...fail(
|
|
7918
|
+
pluginPath,
|
|
7919
|
+
pluginId,
|
|
7920
|
+
"invalid-manifest",
|
|
7921
|
+
tx(PLUGIN_LOADER_TEXTS.invalidManifestAnnotationSchemaCompile, {
|
|
7922
|
+
relEntry,
|
|
7923
|
+
key: "<annotation>",
|
|
7924
|
+
errDescription: describe(err)
|
|
7925
|
+
})
|
|
7926
|
+
),
|
|
7927
|
+
manifest
|
|
7928
|
+
};
|
|
7855
7929
|
}
|
|
7856
7930
|
return null;
|
|
7857
7931
|
}
|
|
7858
|
-
function validateHookTriggers(pluginPath, manifest, relEntry, exported, manifestView) {
|
|
7932
|
+
function validateHookTriggers(pluginPath, pluginId, manifest, relEntry, exported, manifestView) {
|
|
7859
7933
|
const triggers = manifestView["triggers"];
|
|
7860
7934
|
const hookId = exported["id"] ?? "?";
|
|
7861
7935
|
if (!Array.isArray(triggers) || triggers.length === 0) {
|
|
7862
7936
|
return {
|
|
7863
7937
|
...fail(
|
|
7864
7938
|
pluginPath,
|
|
7865
|
-
|
|
7939
|
+
pluginId,
|
|
7866
7940
|
"invalid-manifest",
|
|
7867
7941
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestHookEmptyTriggers, { hookId })
|
|
7868
7942
|
),
|
|
@@ -7874,7 +7948,7 @@ function validateHookTriggers(pluginPath, manifest, relEntry, exported, manifest
|
|
|
7874
7948
|
return {
|
|
7875
7949
|
...fail(
|
|
7876
7950
|
pluginPath,
|
|
7877
|
-
|
|
7951
|
+
pluginId,
|
|
7878
7952
|
"invalid-manifest",
|
|
7879
7953
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestHookUnknownTrigger, {
|
|
7880
7954
|
hookId,
|
|
@@ -7888,9 +7962,135 @@ function validateHookTriggers(pluginPath, manifest, relEntry, exported, manifest
|
|
|
7888
7962
|
}
|
|
7889
7963
|
return null;
|
|
7890
7964
|
}
|
|
7965
|
+
function validateActionFileConventions(pluginPath, pluginId, manifest, relEntry, entryAbsPath, manifestView) {
|
|
7966
|
+
const actionDir = dirname9(entryAbsPath);
|
|
7967
|
+
const reportSchemaPath = join7(actionDir, "report.schema.json");
|
|
7968
|
+
const promptPath = join7(actionDir, "prompt.md");
|
|
7969
|
+
const mode = isRecord(manifestView) && typeof manifestView["mode"] === "string" ? manifestView["mode"] : "deterministic";
|
|
7970
|
+
if (!existsSync11(reportSchemaPath)) {
|
|
7971
|
+
return {
|
|
7972
|
+
...fail(
|
|
7973
|
+
pluginPath,
|
|
7974
|
+
pluginId,
|
|
7975
|
+
"load-error",
|
|
7976
|
+
`Action at \`${relEntry}\` is missing \`report.schema.json\` in its folder (structure-as-truth: every Action carries a report schema by convention).`
|
|
7977
|
+
),
|
|
7978
|
+
manifest
|
|
7979
|
+
};
|
|
7980
|
+
}
|
|
7981
|
+
const promptExists = existsSync11(promptPath);
|
|
7982
|
+
if (mode === "probabilistic" && !promptExists) {
|
|
7983
|
+
return {
|
|
7984
|
+
...fail(
|
|
7985
|
+
pluginPath,
|
|
7986
|
+
pluginId,
|
|
7987
|
+
"load-error",
|
|
7988
|
+
`Probabilistic Action at \`${relEntry}\` is missing \`prompt.md\` in its folder (structure-as-truth: probabilistic Actions carry a prompt template by convention).`
|
|
7989
|
+
),
|
|
7990
|
+
manifest
|
|
7991
|
+
};
|
|
7992
|
+
}
|
|
7993
|
+
if (mode === "deterministic" && promptExists) {
|
|
7994
|
+
return {
|
|
7995
|
+
...fail(
|
|
7996
|
+
pluginPath,
|
|
7997
|
+
pluginId,
|
|
7998
|
+
"invalid-manifest",
|
|
7999
|
+
`Deterministic Action at \`${relEntry}\` carries an unexpected \`prompt.md\` (delete the file or switch \`mode\` to \`'probabilistic'\`).`
|
|
8000
|
+
),
|
|
8001
|
+
manifest
|
|
8002
|
+
};
|
|
8003
|
+
}
|
|
8004
|
+
return null;
|
|
8005
|
+
}
|
|
8006
|
+
function discoverProviderKinds(pluginPath, pluginId, manifest, relEntry, validatorForKind) {
|
|
8007
|
+
const kindsRoot = join7(pluginPath, "kinds");
|
|
8008
|
+
let entries;
|
|
8009
|
+
try {
|
|
8010
|
+
entries = nodeFs.readdirSync(kindsRoot);
|
|
8011
|
+
} catch {
|
|
8012
|
+
return { ok: true, kinds: {} };
|
|
8013
|
+
}
|
|
8014
|
+
const out = {};
|
|
8015
|
+
for (const entry of entries.sort()) {
|
|
8016
|
+
if (entry.startsWith(".")) continue;
|
|
8017
|
+
const kindDir = join7(kindsRoot, entry);
|
|
8018
|
+
if (!isDirectorySafe(kindDir, nodeFs.statSync)) continue;
|
|
8019
|
+
const result = loadOneProviderKind({
|
|
8020
|
+
pluginPath,
|
|
8021
|
+
pluginId,
|
|
8022
|
+
manifest,
|
|
8023
|
+
relEntry,
|
|
8024
|
+
entry,
|
|
8025
|
+
kindDir,
|
|
8026
|
+
validatorForKind
|
|
8027
|
+
});
|
|
8028
|
+
if (!result.ok) return result;
|
|
8029
|
+
out[entry] = result.kind;
|
|
8030
|
+
}
|
|
8031
|
+
return { ok: true, kinds: out };
|
|
8032
|
+
}
|
|
8033
|
+
function loadOneProviderKind(opts) {
|
|
8034
|
+
const schemaJson = readJsonFile(join7(opts.kindDir, "schema.json"));
|
|
8035
|
+
if ("error" in schemaJson) {
|
|
8036
|
+
return providerKindFailure(opts, "load-error", "schema.json", schemaJson.error);
|
|
8037
|
+
}
|
|
8038
|
+
const kindJson = readJsonFile(join7(opts.kindDir, "kind.json"));
|
|
8039
|
+
if ("error" in kindJson) {
|
|
8040
|
+
return providerKindFailure(opts, "invalid-manifest", "kind.json", kindJson.error);
|
|
8041
|
+
}
|
|
8042
|
+
const validation = opts.validatorForKind(kindJson.value);
|
|
8043
|
+
if (!validation.ok) {
|
|
8044
|
+
return {
|
|
8045
|
+
ok: false,
|
|
8046
|
+
failure: {
|
|
8047
|
+
...fail(
|
|
8048
|
+
opts.pluginPath,
|
|
8049
|
+
opts.pluginId,
|
|
8050
|
+
"invalid-manifest",
|
|
8051
|
+
`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.`
|
|
8052
|
+
),
|
|
8053
|
+
manifest: opts.manifest
|
|
8054
|
+
}
|
|
8055
|
+
};
|
|
8056
|
+
}
|
|
8057
|
+
const ui = isRecord(kindJson.value) ? kindJson.value["ui"] : void 0;
|
|
8058
|
+
return {
|
|
8059
|
+
ok: true,
|
|
8060
|
+
kind: { schema: `./kinds/${opts.entry}/schema.json`, schemaJson: schemaJson.value, ui }
|
|
8061
|
+
};
|
|
8062
|
+
}
|
|
8063
|
+
function readJsonFile(path) {
|
|
8064
|
+
try {
|
|
8065
|
+
return { value: JSON.parse(nodeFs.readFileSync(path, "utf8")) };
|
|
8066
|
+
} catch (err) {
|
|
8067
|
+
return { error: describe(err) };
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
function providerKindFailure(opts, status, fileName, errDescription) {
|
|
8071
|
+
return {
|
|
8072
|
+
ok: false,
|
|
8073
|
+
failure: {
|
|
8074
|
+
...fail(
|
|
8075
|
+
opts.pluginPath,
|
|
8076
|
+
opts.pluginId,
|
|
8077
|
+
status,
|
|
8078
|
+
`Provider kind \`${opts.entry}\` (declared at \`${opts.relEntry}\`) is missing or has an unparseable \`kinds/${opts.entry}/${fileName}\` (${errDescription}).`
|
|
8079
|
+
),
|
|
8080
|
+
manifest: opts.manifest
|
|
8081
|
+
}
|
|
8082
|
+
};
|
|
8083
|
+
}
|
|
8084
|
+
function isDirectorySafe(path, statSync11) {
|
|
8085
|
+
try {
|
|
8086
|
+
return statSync11(path).isDirectory();
|
|
8087
|
+
} catch {
|
|
8088
|
+
return false;
|
|
8089
|
+
}
|
|
8090
|
+
}
|
|
7891
8091
|
|
|
7892
8092
|
// kernel/adapters/plugin-loader/storage-schemas.ts
|
|
7893
|
-
import { readFileSync as
|
|
8093
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
7894
8094
|
import { resolve as resolve15 } from "path";
|
|
7895
8095
|
import { Ajv2020 as Ajv20206 } from "ajv/dist/2020.js";
|
|
7896
8096
|
|
|
@@ -7898,7 +8098,7 @@ import { Ajv2020 as Ajv20206 } from "ajv/dist/2020.js";
|
|
|
7898
8098
|
var KV_SCHEMA_KEY = "__kv__";
|
|
7899
8099
|
|
|
7900
8100
|
// kernel/adapters/plugin-loader/storage-schemas.ts
|
|
7901
|
-
function loadStorageSchemas(pluginPath, manifest) {
|
|
8101
|
+
function loadStorageSchemas(pluginPath, pluginId, manifest) {
|
|
7902
8102
|
const storage = manifest.storage;
|
|
7903
8103
|
if (!storage) return { ok: true };
|
|
7904
8104
|
if (storage.mode === "kv") {
|
|
@@ -7908,7 +8108,7 @@ function loadStorageSchemas(pluginPath, manifest) {
|
|
|
7908
8108
|
const reason = tx(
|
|
7909
8109
|
compiled.phase === "read" ? PLUGIN_LOADER_TEXTS.loadErrorStorageKvSchemaRead : PLUGIN_LOADER_TEXTS.loadErrorStorageKvSchemaCompile,
|
|
7910
8110
|
{
|
|
7911
|
-
pluginId
|
|
8111
|
+
pluginId,
|
|
7912
8112
|
schemaPath: storage.schema,
|
|
7913
8113
|
errDescription: compiled.errDescription
|
|
7914
8114
|
}
|
|
@@ -7935,7 +8135,7 @@ function loadStorageSchemas(pluginPath, manifest) {
|
|
|
7935
8135
|
const reason = tx(
|
|
7936
8136
|
compiled.phase === "read" ? PLUGIN_LOADER_TEXTS.loadErrorStorageSchemaRead : PLUGIN_LOADER_TEXTS.loadErrorStorageSchemaCompile,
|
|
7937
8137
|
{
|
|
7938
|
-
pluginId
|
|
8138
|
+
pluginId,
|
|
7939
8139
|
table,
|
|
7940
8140
|
schemaPath: relPath,
|
|
7941
8141
|
errDescription: compiled.errDescription
|
|
@@ -7958,7 +8158,7 @@ function compilePluginSchema(pluginPath, relPath) {
|
|
|
7958
8158
|
const abs = resolve15(pluginPath, relPath);
|
|
7959
8159
|
let raw;
|
|
7960
8160
|
try {
|
|
7961
|
-
raw = JSON.parse(
|
|
8161
|
+
raw = JSON.parse(readFileSync11(abs, "utf8"));
|
|
7962
8162
|
} catch (err) {
|
|
7963
8163
|
return { ok: false, phase: "read", errDescription: describe(err) };
|
|
7964
8164
|
}
|
|
@@ -7992,11 +8192,11 @@ var PluginLoader = class {
|
|
|
7992
8192
|
discoverPaths() {
|
|
7993
8193
|
const out = [];
|
|
7994
8194
|
for (const root of this.#options.searchPaths) {
|
|
7995
|
-
if (!
|
|
7996
|
-
for (const entry of
|
|
8195
|
+
if (!existsSync12(root)) continue;
|
|
8196
|
+
for (const entry of readdirSync4(root, { withFileTypes: true })) {
|
|
7997
8197
|
if (!entry.isDirectory()) continue;
|
|
7998
|
-
const candidate =
|
|
7999
|
-
if (
|
|
8198
|
+
const candidate = join8(root, entry.name);
|
|
8199
|
+
if (existsSync12(join8(candidate, "plugin.json"))) {
|
|
8000
8200
|
out.push(resolve16(candidate));
|
|
8001
8201
|
}
|
|
8002
8202
|
}
|
|
@@ -8006,7 +8206,7 @@ var PluginLoader = class {
|
|
|
8006
8206
|
/**
|
|
8007
8207
|
* Full pass, discover every plugin, attempt to load each, then apply
|
|
8008
8208
|
* the cross-root id-collision pass over the results. Two plugins that
|
|
8009
|
-
* survived their individual load with the same `
|
|
8209
|
+
* survived their individual load with the same `pluginId` both get
|
|
8010
8210
|
* downgraded to status `id-collision` (no precedence, the spec is
|
|
8011
8211
|
* explicit that "no extension is privileged"). Plugins that already
|
|
8012
8212
|
* failed their individual load (`invalid-manifest` /
|
|
@@ -8026,61 +8226,69 @@ var PluginLoader = class {
|
|
|
8026
8226
|
/**
|
|
8027
8227
|
* Load a single plugin from its directory. Never throws, a failure is
|
|
8028
8228
|
* reported via the returned status.
|
|
8229
|
+
*
|
|
8230
|
+
* Cyclomatic count covers the four sequential phases (manifest parse,
|
|
8231
|
+
* enabled resolution, per-extension load loop, storage output-schemas
|
|
8232
|
+
* compile) plus their failure short-circuits. Splitting each phase
|
|
8233
|
+
* into a helper would scatter the return-on-failure pattern without
|
|
8234
|
+
* making the orchestration clearer.
|
|
8029
8235
|
*/
|
|
8030
8236
|
// eslint-disable-next-line complexity
|
|
8031
8237
|
async loadOne(pluginPath) {
|
|
8032
|
-
const
|
|
8238
|
+
const pluginId = pathId(pluginPath);
|
|
8239
|
+
const manifestResult = this.#parseAndValidateManifest(pluginPath, pluginId);
|
|
8033
8240
|
if (!manifestResult.ok) return manifestResult.failure;
|
|
8034
8241
|
const manifest = manifestResult.manifest;
|
|
8035
|
-
|
|
8242
|
+
const granularity = manifest.granularity ?? "extension";
|
|
8243
|
+
if (this.#options.resolveEnabled && !this.#options.resolveEnabled(pluginId)) {
|
|
8036
8244
|
return {
|
|
8037
8245
|
path: pluginPath,
|
|
8038
|
-
id:
|
|
8246
|
+
id: pluginId,
|
|
8039
8247
|
status: "disabled",
|
|
8040
8248
|
manifest,
|
|
8041
|
-
granularity
|
|
8249
|
+
granularity,
|
|
8042
8250
|
reason: PLUGIN_LOADER_TEXTS.disabledByConfig
|
|
8043
8251
|
};
|
|
8044
8252
|
}
|
|
8045
8253
|
const loaded = [];
|
|
8046
|
-
for (const relEntry of
|
|
8047
|
-
const result = await this.#loadAndValidateExtensionEntry(pluginPath, manifest, relEntry);
|
|
8254
|
+
for (const relEntry of discoverExtensionEntries(pluginPath)) {
|
|
8255
|
+
const result = await this.#loadAndValidateExtensionEntry(pluginPath, pluginId, manifest, relEntry);
|
|
8048
8256
|
if (!result.ok) return result.failure;
|
|
8049
8257
|
loaded.push(result.extension);
|
|
8050
8258
|
}
|
|
8051
|
-
const storageSchemasResult = loadStorageSchemas(pluginPath, manifest);
|
|
8259
|
+
const storageSchemasResult = loadStorageSchemas(pluginPath, pluginId, manifest);
|
|
8052
8260
|
if (!storageSchemasResult.ok) {
|
|
8053
8261
|
return {
|
|
8054
|
-
...fail(pluginPath,
|
|
8262
|
+
...fail(pluginPath, pluginId, "load-error", storageSchemasResult.reason),
|
|
8055
8263
|
manifest
|
|
8056
8264
|
};
|
|
8057
8265
|
}
|
|
8058
8266
|
return {
|
|
8059
8267
|
path: pluginPath,
|
|
8060
|
-
id:
|
|
8268
|
+
id: pluginId,
|
|
8061
8269
|
status: "enabled",
|
|
8062
8270
|
manifest,
|
|
8063
|
-
granularity
|
|
8271
|
+
granularity,
|
|
8064
8272
|
extensions: loaded,
|
|
8065
8273
|
...storageSchemasResult.schemas ? { storageSchemas: storageSchemasResult.schemas } : {}
|
|
8066
8274
|
};
|
|
8067
8275
|
}
|
|
8068
8276
|
/**
|
|
8069
8277
|
* Phase 1 of `loadOne`, read `plugin.json`, AJV-validate the manifest,
|
|
8070
|
-
* enforce the directory-name ==
|
|
8278
|
+
* enforce the directory-name == pluginId structural rule, and check
|
|
8071
8279
|
* specCompat (range syntax + satisfies the installed spec version).
|
|
8072
8280
|
* Returns either the validated manifest or an `IDiscoveredPlugin` with
|
|
8073
8281
|
* the appropriate failure status.
|
|
8074
8282
|
*/
|
|
8075
|
-
#parseAndValidateManifest(pluginPath) {
|
|
8076
|
-
const manifestPath =
|
|
8283
|
+
#parseAndValidateManifest(pluginPath, pluginId) {
|
|
8284
|
+
const manifestPath = join8(pluginPath, "plugin.json");
|
|
8077
8285
|
let raw;
|
|
8078
8286
|
try {
|
|
8079
|
-
raw = JSON.parse(
|
|
8287
|
+
raw = JSON.parse(readFileSync12(manifestPath, "utf8"));
|
|
8080
8288
|
} catch (err) {
|
|
8081
8289
|
return { ok: false, failure: fail(
|
|
8082
8290
|
pluginPath,
|
|
8083
|
-
|
|
8291
|
+
pluginId,
|
|
8084
8292
|
"invalid-manifest",
|
|
8085
8293
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestJsonParse, {
|
|
8086
8294
|
manifestPath,
|
|
@@ -8092,7 +8300,7 @@ var PluginLoader = class {
|
|
|
8092
8300
|
if (!manifestResult.ok) {
|
|
8093
8301
|
return { ok: false, failure: fail(
|
|
8094
8302
|
pluginPath,
|
|
8095
|
-
|
|
8303
|
+
pluginId,
|
|
8096
8304
|
"invalid-manifest",
|
|
8097
8305
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestAjv, {
|
|
8098
8306
|
manifestPath,
|
|
@@ -8101,26 +8309,11 @@ var PluginLoader = class {
|
|
|
8101
8309
|
) };
|
|
8102
8310
|
}
|
|
8103
8311
|
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
8312
|
if (!semver.validRange(manifest.specCompat)) {
|
|
8120
8313
|
return { ok: false, failure: {
|
|
8121
8314
|
...fail(
|
|
8122
8315
|
pluginPath,
|
|
8123
|
-
|
|
8316
|
+
pluginId,
|
|
8124
8317
|
"invalid-manifest",
|
|
8125
8318
|
tx(PLUGIN_LOADER_TEXTS.invalidSpecCompat, { specCompat: manifest.specCompat })
|
|
8126
8319
|
),
|
|
@@ -8130,10 +8323,10 @@ var PluginLoader = class {
|
|
|
8130
8323
|
if (!semver.satisfies(this.#options.specVersion, manifest.specCompat, { includePrerelease: true })) {
|
|
8131
8324
|
return { ok: false, failure: {
|
|
8132
8325
|
path: pluginPath,
|
|
8133
|
-
id:
|
|
8326
|
+
id: pluginId,
|
|
8134
8327
|
status: "incompatible-spec",
|
|
8135
8328
|
manifest,
|
|
8136
|
-
granularity: manifest.granularity ?? "
|
|
8329
|
+
granularity: manifest.granularity ?? "extension",
|
|
8137
8330
|
reason: tx(PLUGIN_LOADER_TEXTS.incompatibleSpec, {
|
|
8138
8331
|
installedSpecVersion: this.#options.specVersion,
|
|
8139
8332
|
specCompat: manifest.specCompat
|
|
@@ -8156,12 +8349,12 @@ var PluginLoader = class {
|
|
|
8156
8349
|
// splitting per sub-check would multiply the discriminated-union
|
|
8157
8350
|
// boilerplate without making the validation pipeline clearer.
|
|
8158
8351
|
// eslint-disable-next-line complexity
|
|
8159
|
-
async #loadAndValidateExtensionEntry(pluginPath, manifest, relEntry) {
|
|
8352
|
+
async #loadAndValidateExtensionEntry(pluginPath, pluginId, manifest, relEntry) {
|
|
8160
8353
|
if (!isInsidePlugin(pluginPath, relEntry)) {
|
|
8161
8354
|
return { ok: false, failure: {
|
|
8162
8355
|
...fail(
|
|
8163
8356
|
pluginPath,
|
|
8164
|
-
|
|
8357
|
+
pluginId,
|
|
8165
8358
|
"invalid-manifest",
|
|
8166
8359
|
tx(PLUGIN_LOADER_TEXTS.loadErrorPathEscapesPlugin, { relEntry, pluginPath })
|
|
8167
8360
|
),
|
|
@@ -8169,11 +8362,11 @@ var PluginLoader = class {
|
|
|
8169
8362
|
} };
|
|
8170
8363
|
}
|
|
8171
8364
|
const abs = resolve16(pluginPath, relEntry);
|
|
8172
|
-
if (!
|
|
8365
|
+
if (!existsSync12(abs)) {
|
|
8173
8366
|
return { ok: false, failure: {
|
|
8174
8367
|
...fail(
|
|
8175
8368
|
pluginPath,
|
|
8176
|
-
|
|
8369
|
+
pluginId,
|
|
8177
8370
|
"load-error",
|
|
8178
8371
|
tx(PLUGIN_LOADER_TEXTS.loadErrorFileNotFound, { relEntry, abs })
|
|
8179
8372
|
),
|
|
@@ -8187,7 +8380,7 @@ var PluginLoader = class {
|
|
|
8187
8380
|
return { ok: false, failure: {
|
|
8188
8381
|
...fail(
|
|
8189
8382
|
pluginPath,
|
|
8190
|
-
|
|
8383
|
+
pluginId,
|
|
8191
8384
|
"load-error",
|
|
8192
8385
|
tx(PLUGIN_LOADER_TEXTS.loadErrorImportFailed, {
|
|
8193
8386
|
relEntry,
|
|
@@ -8198,11 +8391,11 @@ var PluginLoader = class {
|
|
|
8198
8391
|
} };
|
|
8199
8392
|
}
|
|
8200
8393
|
const exported = extractDefault(mod);
|
|
8201
|
-
if (!isRecord(exported)
|
|
8394
|
+
if (!isRecord(exported)) {
|
|
8202
8395
|
return { ok: false, failure: {
|
|
8203
8396
|
...fail(
|
|
8204
8397
|
pluginPath,
|
|
8205
|
-
|
|
8398
|
+
pluginId,
|
|
8206
8399
|
"load-error",
|
|
8207
8400
|
tx(PLUGIN_LOADER_TEXTS.loadErrorMissingKind, {
|
|
8208
8401
|
relEntry,
|
|
@@ -8212,16 +8405,32 @@ var PluginLoader = class {
|
|
|
8212
8405
|
manifest
|
|
8213
8406
|
} };
|
|
8214
8407
|
}
|
|
8215
|
-
const
|
|
8216
|
-
|
|
8408
|
+
const [pathKindDir, pathId2] = relEntry.split("/");
|
|
8409
|
+
const kindFromPath = pathKindDir && pathKindDir.endsWith("s") ? pathKindDir.slice(0, -1) : void 0;
|
|
8410
|
+
if (!kindFromPath || !KNOWN_KINDS.has(kindFromPath)) {
|
|
8217
8411
|
return { ok: false, failure: {
|
|
8218
8412
|
...fail(
|
|
8219
8413
|
pluginPath,
|
|
8220
|
-
|
|
8221
|
-
"
|
|
8414
|
+
pluginId,
|
|
8415
|
+
"invalid-manifest",
|
|
8222
8416
|
tx(PLUGIN_LOADER_TEXTS.loadErrorUnknownKind, {
|
|
8223
8417
|
relEntry,
|
|
8224
|
-
kindReceived: String(
|
|
8418
|
+
kindReceived: String(pathKindDir ?? "(missing)"),
|
|
8419
|
+
knownKindsList: KNOWN_KINDS_LIST
|
|
8420
|
+
})
|
|
8421
|
+
),
|
|
8422
|
+
manifest
|
|
8423
|
+
} };
|
|
8424
|
+
}
|
|
8425
|
+
const kind = kindFromPath;
|
|
8426
|
+
if (!pathId2) {
|
|
8427
|
+
return { ok: false, failure: {
|
|
8428
|
+
...fail(
|
|
8429
|
+
pluginPath,
|
|
8430
|
+
pluginId,
|
|
8431
|
+
"invalid-manifest",
|
|
8432
|
+
tx(PLUGIN_LOADER_TEXTS.loadErrorMissingKind, {
|
|
8433
|
+
relEntry,
|
|
8225
8434
|
knownKindsList: KNOWN_KINDS_LIST
|
|
8226
8435
|
})
|
|
8227
8436
|
),
|
|
@@ -8229,16 +8438,16 @@ var PluginLoader = class {
|
|
|
8229
8438
|
} };
|
|
8230
8439
|
}
|
|
8231
8440
|
const declaredPluginId = exported["pluginId"];
|
|
8232
|
-
if (typeof declaredPluginId === "string" && declaredPluginId !==
|
|
8441
|
+
if (typeof declaredPluginId === "string" && declaredPluginId !== pluginId) {
|
|
8233
8442
|
return { ok: false, failure: {
|
|
8234
8443
|
...fail(
|
|
8235
8444
|
pluginPath,
|
|
8236
|
-
|
|
8445
|
+
pluginId,
|
|
8237
8446
|
"invalid-manifest",
|
|
8238
8447
|
tx(PLUGIN_LOADER_TEXTS.loadErrorPluginIdMismatch, {
|
|
8239
8448
|
relEntry,
|
|
8240
8449
|
declared: declaredPluginId,
|
|
8241
|
-
manifestId:
|
|
8450
|
+
manifestId: pluginId
|
|
8242
8451
|
})
|
|
8243
8452
|
),
|
|
8244
8453
|
manifest
|
|
@@ -8246,7 +8455,7 @@ var PluginLoader = class {
|
|
|
8246
8455
|
}
|
|
8247
8456
|
const manifestView = stripFunctionsAndPluginId(exported);
|
|
8248
8457
|
if (kind === "hook") {
|
|
8249
|
-
const hookFailure = validateHookTriggers(pluginPath, manifest, relEntry, exported, manifestView);
|
|
8458
|
+
const hookFailure = validateHookTriggers(pluginPath, pluginId, manifest, relEntry, exported, manifestView);
|
|
8250
8459
|
if (hookFailure) return { ok: false, failure: hookFailure };
|
|
8251
8460
|
}
|
|
8252
8461
|
const extValidator = this.#options.validators.validatorForExtension(kind);
|
|
@@ -8255,7 +8464,7 @@ var PluginLoader = class {
|
|
|
8255
8464
|
return { ok: false, failure: {
|
|
8256
8465
|
...fail(
|
|
8257
8466
|
pluginPath,
|
|
8258
|
-
|
|
8467
|
+
pluginId,
|
|
8259
8468
|
"invalid-manifest",
|
|
8260
8469
|
tx(PLUGIN_LOADER_TEXTS.invalidManifestExtensionShape, { relEntry, kind, errors })
|
|
8261
8470
|
),
|
|
@@ -8264,16 +8473,49 @@ var PluginLoader = class {
|
|
|
8264
8473
|
}
|
|
8265
8474
|
const contribFailure = validateAnnotationContributions(
|
|
8266
8475
|
pluginPath,
|
|
8476
|
+
pluginId,
|
|
8267
8477
|
manifest,
|
|
8268
8478
|
relEntry,
|
|
8269
8479
|
manifestView
|
|
8270
8480
|
);
|
|
8271
8481
|
if (contribFailure) return { ok: false, failure: contribFailure };
|
|
8272
|
-
|
|
8482
|
+
if (kind === "action") {
|
|
8483
|
+
const actionFailure = validateActionFileConventions(
|
|
8484
|
+
pluginPath,
|
|
8485
|
+
pluginId,
|
|
8486
|
+
manifest,
|
|
8487
|
+
relEntry,
|
|
8488
|
+
abs,
|
|
8489
|
+
manifestView
|
|
8490
|
+
);
|
|
8491
|
+
if (actionFailure) return { ok: false, failure: actionFailure };
|
|
8492
|
+
}
|
|
8493
|
+
let discoveredKinds;
|
|
8494
|
+
if (kind === "provider") {
|
|
8495
|
+
const kindsResult = discoverProviderKinds(
|
|
8496
|
+
pluginPath,
|
|
8497
|
+
pluginId,
|
|
8498
|
+
manifest,
|
|
8499
|
+
relEntry,
|
|
8500
|
+
(data) => {
|
|
8501
|
+
const v = this.#options.validators.validate("extension-provider-kind", data);
|
|
8502
|
+
if (v.ok) return { ok: true, errors: "" };
|
|
8503
|
+
return { ok: false, errors: v.errors };
|
|
8504
|
+
}
|
|
8505
|
+
);
|
|
8506
|
+
if (!kindsResult.ok) return { ok: false, failure: kindsResult.failure };
|
|
8507
|
+
if (Object.keys(kindsResult.kinds).length > 0) discoveredKinds = kindsResult.kinds;
|
|
8508
|
+
}
|
|
8509
|
+
const instance = { ...exported, pluginId, id: pathId2, kind };
|
|
8510
|
+
if (kind === "formatter") instance["formatId"] = pathId2;
|
|
8511
|
+
if (kind === "provider" && discoveredKinds) {
|
|
8512
|
+
const inlineKinds = isRecord(exported["kinds"]) ? exported["kinds"] : {};
|
|
8513
|
+
instance["kinds"] = { ...inlineKinds, ...discoveredKinds };
|
|
8514
|
+
}
|
|
8273
8515
|
return { ok: true, extension: {
|
|
8274
8516
|
kind,
|
|
8275
|
-
id:
|
|
8276
|
-
pluginId
|
|
8517
|
+
id: pathId2,
|
|
8518
|
+
pluginId,
|
|
8277
8519
|
version: exported["version"],
|
|
8278
8520
|
entryPath: abs,
|
|
8279
8521
|
module: mod,
|
|
@@ -8281,11 +8523,64 @@ var PluginLoader = class {
|
|
|
8281
8523
|
} };
|
|
8282
8524
|
}
|
|
8283
8525
|
};
|
|
8526
|
+
var KIND_DIR_NAMES = [
|
|
8527
|
+
"providers",
|
|
8528
|
+
"extractors",
|
|
8529
|
+
"analyzers",
|
|
8530
|
+
"actions",
|
|
8531
|
+
"formatters",
|
|
8532
|
+
"hooks"
|
|
8533
|
+
];
|
|
8534
|
+
var INDEX_CANDIDATES = [
|
|
8535
|
+
"index.js",
|
|
8536
|
+
"index.mjs",
|
|
8537
|
+
"index.ts"
|
|
8538
|
+
];
|
|
8539
|
+
function discoverExtensionEntries(pluginPath) {
|
|
8540
|
+
const out = [];
|
|
8541
|
+
for (const kindDir of KIND_DIR_NAMES) {
|
|
8542
|
+
collectKindEntries(pluginPath, kindDir, out);
|
|
8543
|
+
}
|
|
8544
|
+
return out;
|
|
8545
|
+
}
|
|
8546
|
+
function collectKindEntries(pluginPath, kindDir, out) {
|
|
8547
|
+
const kindAbs = resolve16(pluginPath, kindDir);
|
|
8548
|
+
if (!existsSync12(kindAbs)) return;
|
|
8549
|
+
let entries;
|
|
8550
|
+
try {
|
|
8551
|
+
entries = readdirSync4(kindAbs);
|
|
8552
|
+
} catch {
|
|
8553
|
+
return;
|
|
8554
|
+
}
|
|
8555
|
+
entries.sort();
|
|
8556
|
+
for (const entry of entries) {
|
|
8557
|
+
if (entry.startsWith(".")) continue;
|
|
8558
|
+
const entryAbs = resolve16(kindAbs, entry);
|
|
8559
|
+
if (!isDirectorySafe2(entryAbs)) continue;
|
|
8560
|
+
const candidate = findIndexCandidate(entryAbs);
|
|
8561
|
+
if (candidate !== null) {
|
|
8562
|
+
out.push(`${kindDir}/${entry}/${candidate}`);
|
|
8563
|
+
}
|
|
8564
|
+
}
|
|
8565
|
+
}
|
|
8566
|
+
function isDirectorySafe2(path) {
|
|
8567
|
+
try {
|
|
8568
|
+
return statSync2(path).isDirectory();
|
|
8569
|
+
} catch {
|
|
8570
|
+
return false;
|
|
8571
|
+
}
|
|
8572
|
+
}
|
|
8573
|
+
function findIndexCandidate(entryAbs) {
|
|
8574
|
+
for (const candidate of INDEX_CANDIDATES) {
|
|
8575
|
+
if (existsSync12(resolve16(entryAbs, candidate))) return candidate;
|
|
8576
|
+
}
|
|
8577
|
+
return null;
|
|
8578
|
+
}
|
|
8284
8579
|
function installedSpecVersion() {
|
|
8285
8580
|
const require2 = createRequire5(import.meta.url);
|
|
8286
8581
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
8287
8582
|
const pkgPath = resolve16(indexPath, "..", "package.json");
|
|
8288
|
-
const pkg = JSON.parse(
|
|
8583
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf8"));
|
|
8289
8584
|
return pkg.version;
|
|
8290
8585
|
}
|
|
8291
8586
|
|
|
@@ -8382,11 +8677,11 @@ async function buildEnabledResolver(ctx) {
|
|
|
8382
8677
|
|
|
8383
8678
|
// kernel/scan/walk-content.ts
|
|
8384
8679
|
import { readFile, readdir, lstat } from "fs/promises";
|
|
8385
|
-
import { join as
|
|
8680
|
+
import { join as join9, relative as relative2, sep as sep2 } from "path";
|
|
8386
8681
|
|
|
8387
8682
|
// kernel/scan/ignore.ts
|
|
8388
|
-
import { existsSync as
|
|
8389
|
-
import { dirname as
|
|
8683
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
|
|
8684
|
+
import { dirname as dirname10, resolve as resolve17 } from "path";
|
|
8390
8685
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8391
8686
|
import ignoreFactory from "ignore";
|
|
8392
8687
|
function buildIgnoreFilter(opts = {}) {
|
|
@@ -8416,9 +8711,9 @@ function loadBundledIgnoreText() {
|
|
|
8416
8711
|
}
|
|
8417
8712
|
function readIgnoreFileText(scopeRoot) {
|
|
8418
8713
|
const path = resolve17(scopeRoot, ".skillmapignore");
|
|
8419
|
-
if (!
|
|
8714
|
+
if (!existsSync13(path)) return void 0;
|
|
8420
8715
|
try {
|
|
8421
|
-
return
|
|
8716
|
+
return readFileSync13(path, "utf8");
|
|
8422
8717
|
} catch {
|
|
8423
8718
|
return void 0;
|
|
8424
8719
|
}
|
|
@@ -8442,7 +8737,7 @@ function loadDefaultsText() {
|
|
|
8442
8737
|
return cachedDefaults;
|
|
8443
8738
|
}
|
|
8444
8739
|
function readDefaultsFromDisk() {
|
|
8445
|
-
const here =
|
|
8740
|
+
const here = dirname10(fileURLToPath2(import.meta.url));
|
|
8446
8741
|
const candidates = [
|
|
8447
8742
|
resolve17(here, "../../config/defaults/skillmapignore"),
|
|
8448
8743
|
// src/kernel/scan/ → src/config/defaults/
|
|
@@ -8451,9 +8746,9 @@ function readDefaultsFromDisk() {
|
|
|
8451
8746
|
resolve17(here, "config/defaults/skillmapignore")
|
|
8452
8747
|
];
|
|
8453
8748
|
for (const candidate of candidates) {
|
|
8454
|
-
if (
|
|
8749
|
+
if (existsSync13(candidate)) {
|
|
8455
8750
|
try {
|
|
8456
|
-
return
|
|
8751
|
+
return readFileSync13(candidate, "utf8");
|
|
8457
8752
|
} catch {
|
|
8458
8753
|
}
|
|
8459
8754
|
}
|
|
@@ -8461,7 +8756,7 @@ function readDefaultsFromDisk() {
|
|
|
8461
8756
|
return "";
|
|
8462
8757
|
}
|
|
8463
8758
|
|
|
8464
|
-
//
|
|
8759
|
+
// plugins/core/parsers/frontmatter-yaml/index.ts
|
|
8465
8760
|
import yaml3 from "js-yaml";
|
|
8466
8761
|
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
8467
8762
|
var frontmatterYamlParser = {
|
|
@@ -8496,7 +8791,7 @@ function sanitiseParseErrorMessage(err) {
|
|
|
8496
8791
|
return raw.replace(/[-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
8497
8792
|
}
|
|
8498
8793
|
|
|
8499
|
-
//
|
|
8794
|
+
// plugins/core/parsers/plain/index.ts
|
|
8500
8795
|
var plainParser = {
|
|
8501
8796
|
id: "plain",
|
|
8502
8797
|
parse(raw, _path) {
|
|
@@ -8559,7 +8854,7 @@ async function* walkRoot(root, current, filter, extensions) {
|
|
|
8559
8854
|
}
|
|
8560
8855
|
for (const entry of entries) {
|
|
8561
8856
|
const name = entry.name;
|
|
8562
|
-
const full =
|
|
8857
|
+
const full = join9(current, name);
|
|
8563
8858
|
const rel = relative2(root, full).split(sep2).join("/");
|
|
8564
8859
|
if (filter.ignores(rel)) continue;
|
|
8565
8860
|
if (entry.isSymbolicLink()) continue;
|
|
@@ -8605,7 +8900,7 @@ function resolveProviderWalk(provider) {
|
|
|
8605
8900
|
// kernel/extensions/collect-view-contributions.ts
|
|
8606
8901
|
function collectViewContributions(pluginId, extensionId, instance, out, options = {}) {
|
|
8607
8902
|
if (typeof instance !== "object" || instance === null) return;
|
|
8608
|
-
const raw = instance["
|
|
8903
|
+
const raw = instance["ui"];
|
|
8609
8904
|
if (typeof raw !== "object" || raw === null) return;
|
|
8610
8905
|
const exclude = options.excludeQualifiedIds;
|
|
8611
8906
|
for (const [contributionId, value] of Object.entries(raw)) {
|
|
@@ -8649,6 +8944,7 @@ function bucketLoaded(loaded, bundle) {
|
|
|
8649
8944
|
pluginId: ext.pluginId,
|
|
8650
8945
|
kind: ext.kind,
|
|
8651
8946
|
version: ext.version,
|
|
8947
|
+
description: instance.description ?? "",
|
|
8652
8948
|
...ext.entryPath ? { entry: ext.entryPath } : {}
|
|
8653
8949
|
});
|
|
8654
8950
|
collectAnnotationContributions(ext.pluginId, instance, bundle.annotationContributions);
|
|
@@ -8656,21 +8952,25 @@ function bucketLoaded(loaded, bundle) {
|
|
|
8656
8952
|
}
|
|
8657
8953
|
}
|
|
8658
8954
|
function collectAnnotationContributions(pluginId, instance, out) {
|
|
8659
|
-
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8955
|
+
const row = tryReadAnnotationRow(pluginId, instance);
|
|
8956
|
+
if (row !== null) out.push(row);
|
|
8957
|
+
}
|
|
8958
|
+
function tryReadAnnotationRow(pluginId, instance) {
|
|
8959
|
+
if (typeof instance !== "object" || instance === null) return null;
|
|
8960
|
+
const inst = instance;
|
|
8961
|
+
const raw = inst["annotation"];
|
|
8962
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
8963
|
+
const entry = raw;
|
|
8964
|
+
if (typeof entry.schema !== "object" || entry.schema === null) return null;
|
|
8965
|
+
const extId = inst["id"];
|
|
8966
|
+
if (typeof extId !== "string" || extId.length === 0) return null;
|
|
8967
|
+
return {
|
|
8968
|
+
pluginId,
|
|
8969
|
+
key: extId,
|
|
8970
|
+
location: entry.location ?? "namespaced",
|
|
8971
|
+
ownership: entry.ownership ?? "shared",
|
|
8972
|
+
schema: entry.schema
|
|
8973
|
+
};
|
|
8674
8974
|
}
|
|
8675
8975
|
function isExtensionInstance(v) {
|
|
8676
8976
|
return typeof v === "object" && v !== null && typeof v["id"] === "string" && typeof v["kind"] === "string" && typeof v["version"] === "string";
|
|
@@ -8821,7 +9121,7 @@ function collectRegisteredContributionKeys(composed) {
|
|
|
8821
9121
|
const keys = /* @__PURE__ */ new Set();
|
|
8822
9122
|
if (!composed) return keys;
|
|
8823
9123
|
for (const ext of [...composed.extractors, ...composed.analyzers]) {
|
|
8824
|
-
const raw = ext.
|
|
9124
|
+
const raw = ext.ui;
|
|
8825
9125
|
if (typeof raw !== "object" || raw === null) continue;
|
|
8826
9126
|
for (const [contributionId, value] of Object.entries(raw)) {
|
|
8827
9127
|
if (typeof value !== "object" || value === null) continue;
|
|
@@ -9162,7 +9462,7 @@ function trimRedundantPath(message, primary) {
|
|
|
9162
9462
|
}
|
|
9163
9463
|
|
|
9164
9464
|
// cli/commands/config.ts
|
|
9165
|
-
import { existsSync as
|
|
9465
|
+
import { existsSync as existsSync14 } from "fs";
|
|
9166
9466
|
import { Command as Command4, Option as Option4 } from "clipanion";
|
|
9167
9467
|
|
|
9168
9468
|
// cli/util/path-display.ts
|
|
@@ -9690,7 +9990,7 @@ var ConfigResetCommand = class extends SmCommand {
|
|
|
9690
9990
|
const path = targetSettingsPath2(target, ctx.cwd);
|
|
9691
9991
|
const ansi = this.ansiFor("stdout");
|
|
9692
9992
|
const okGlyph = ansi.green("\u2713");
|
|
9693
|
-
if (!
|
|
9993
|
+
if (!existsSync14(path)) {
|
|
9694
9994
|
this.printer.data(
|
|
9695
9995
|
tx(CONFIG_TEXTS.unsetNoOverride, {
|
|
9696
9996
|
glyph: okGlyph,
|
|
@@ -9765,16 +10065,16 @@ var CONFIG_COMMANDS = [
|
|
|
9765
10065
|
];
|
|
9766
10066
|
|
|
9767
10067
|
// cli/commands/conformance.ts
|
|
9768
|
-
import { existsSync as
|
|
9769
|
-
import { dirname as
|
|
10068
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
10069
|
+
import { dirname as dirname12, resolve as resolve21 } from "path";
|
|
9770
10070
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
9771
10071
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
9772
10072
|
|
|
9773
10073
|
// conformance/index.ts
|
|
9774
10074
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9775
|
-
import { cpSync, existsSync as
|
|
10075
|
+
import { cpSync, existsSync as existsSync15, mkdtempSync, readdirSync as readdirSync5, readFileSync as readFileSync14, rmSync, statSync as statSync3 } from "fs";
|
|
9776
10076
|
import { tmpdir } from "os";
|
|
9777
|
-
import { isAbsolute as isAbsolute5, join as
|
|
10077
|
+
import { isAbsolute as isAbsolute5, join as join10, relative as relative3, resolve as resolve19 } from "path";
|
|
9778
10078
|
|
|
9779
10079
|
// conformance/i18n/runner.texts.ts
|
|
9780
10080
|
var CONFORMANCE_RUNNER_TEXTS = {
|
|
@@ -9808,11 +10108,11 @@ function disableEnv(setup) {
|
|
|
9808
10108
|
return env;
|
|
9809
10109
|
}
|
|
9810
10110
|
function runConformanceCase(options) {
|
|
9811
|
-
const raw =
|
|
10111
|
+
const raw = readFileSync14(options.casePath, "utf8");
|
|
9812
10112
|
const c = JSON.parse(raw);
|
|
9813
|
-
const fixturesRoot = options.fixturesRoot ??
|
|
10113
|
+
const fixturesRoot = options.fixturesRoot ?? join10(options.specRoot, "conformance", "fixtures");
|
|
9814
10114
|
const safeId = c.id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 32);
|
|
9815
|
-
const scope = mkdtempSync(
|
|
10115
|
+
const scope = mkdtempSync(join10(tmpdir(), `sm-conformance-${safeId}-`));
|
|
9816
10116
|
const setupEnv = disableEnv(c.setup);
|
|
9817
10117
|
try {
|
|
9818
10118
|
const priorFailure = runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv);
|
|
@@ -9882,11 +10182,11 @@ function runPriorScansSetup(c, options, scope, fixturesRoot, setupEnv) {
|
|
|
9882
10182
|
}
|
|
9883
10183
|
function replaceFixture(scope, fixturesRoot, fixture) {
|
|
9884
10184
|
assertContained2(fixturesRoot, fixture, "fixture");
|
|
9885
|
-
for (const entry of
|
|
10185
|
+
for (const entry of readdirSync5(scope)) {
|
|
9886
10186
|
if (entry === KERNEL_SKILL_MAP_DIR) continue;
|
|
9887
|
-
rmSync(
|
|
10187
|
+
rmSync(join10(scope, entry), { recursive: true, force: true });
|
|
9888
10188
|
}
|
|
9889
|
-
const src =
|
|
10189
|
+
const src = join10(fixturesRoot, fixture);
|
|
9890
10190
|
cpSync(src, scope, { recursive: true });
|
|
9891
10191
|
}
|
|
9892
10192
|
function assertContained2(root, rel, label) {
|
|
@@ -9923,7 +10223,7 @@ function evaluateAssertion(a, ctx) {
|
|
|
9923
10223
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
9924
10224
|
}
|
|
9925
10225
|
const abs = resolve19(ctx.scope, a.path);
|
|
9926
|
-
return
|
|
10226
|
+
return existsSync15(abs) ? { ok: true, type: a.type } : {
|
|
9927
10227
|
ok: false,
|
|
9928
10228
|
type: a.type,
|
|
9929
10229
|
reason: tx(CONFORMANCE_RUNNER_TEXTS.fileNotFound, { path: a.path })
|
|
@@ -9936,17 +10236,17 @@ function evaluateAssertion(a, ctx) {
|
|
|
9936
10236
|
} catch (err) {
|
|
9937
10237
|
return { ok: false, type: a.type, reason: formatErrorMessage(err) };
|
|
9938
10238
|
}
|
|
9939
|
-
const fixturePath =
|
|
10239
|
+
const fixturePath = join10(ctx.fixturesRoot, a.fixture);
|
|
9940
10240
|
const targetPath = resolve19(ctx.scope, a.path);
|
|
9941
|
-
if (!
|
|
10241
|
+
if (!existsSync15(targetPath)) {
|
|
9942
10242
|
return {
|
|
9943
10243
|
ok: false,
|
|
9944
10244
|
type: a.type,
|
|
9945
10245
|
reason: tx(CONFORMANCE_RUNNER_TEXTS.targetNotFound, { path: a.path })
|
|
9946
10246
|
};
|
|
9947
10247
|
}
|
|
9948
|
-
const needle =
|
|
9949
|
-
const haystack =
|
|
10248
|
+
const needle = readFileSync14(fixturePath);
|
|
10249
|
+
const haystack = readFileSync14(targetPath);
|
|
9950
10250
|
return haystack.includes(needle) ? { ok: true, type: a.type } : {
|
|
9951
10251
|
ok: false,
|
|
9952
10252
|
type: a.type,
|
|
@@ -10119,15 +10419,15 @@ var CONFORMANCE_TEXTS = {
|
|
|
10119
10419
|
};
|
|
10120
10420
|
|
|
10121
10421
|
// cli/util/conformance-scopes.ts
|
|
10122
|
-
import { existsSync as
|
|
10123
|
-
import { dirname as
|
|
10422
|
+
import { existsSync as existsSync16, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
|
|
10423
|
+
import { dirname as dirname11, resolve as resolve20 } from "path";
|
|
10124
10424
|
import { createRequire as createRequire6 } from "module";
|
|
10125
10425
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10126
10426
|
function resolveSpecRoot4() {
|
|
10127
10427
|
const require2 = createRequire6(import.meta.url);
|
|
10128
10428
|
try {
|
|
10129
10429
|
const indexPath = require2.resolve("@skill-map/spec/index.json");
|
|
10130
|
-
return
|
|
10430
|
+
return dirname11(indexPath);
|
|
10131
10431
|
} catch {
|
|
10132
10432
|
throw new Error(
|
|
10133
10433
|
"@skill-map/spec not resolvable: ensure the workspace is linked or the package is installed."
|
|
@@ -10135,19 +10435,19 @@ function resolveSpecRoot4() {
|
|
|
10135
10435
|
}
|
|
10136
10436
|
}
|
|
10137
10437
|
function resolveCliWorkspaceRoot() {
|
|
10138
|
-
const here =
|
|
10438
|
+
const here = dirname11(fileURLToPath3(import.meta.url));
|
|
10139
10439
|
let cursor = here;
|
|
10140
10440
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
10141
|
-
const candidate = resolve20(cursor, "
|
|
10142
|
-
if (
|
|
10441
|
+
const candidate = resolve20(cursor, "plugins");
|
|
10442
|
+
if (existsSync16(candidate) && statSync4(candidate).isDirectory()) {
|
|
10143
10443
|
return cursor;
|
|
10144
10444
|
}
|
|
10145
|
-
const parent =
|
|
10445
|
+
const parent = dirname11(cursor);
|
|
10146
10446
|
if (parent === cursor) break;
|
|
10147
10447
|
cursor = parent;
|
|
10148
10448
|
}
|
|
10149
10449
|
throw new Error(
|
|
10150
|
-
`sm conformance: built-in Provider conformance assets not found (expected a '
|
|
10450
|
+
`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
10451
|
);
|
|
10152
10452
|
}
|
|
10153
10453
|
function collectProviderScopes(specRoot) {
|
|
@@ -10158,16 +10458,33 @@ function collectProviderScopes(specRoot) {
|
|
|
10158
10458
|
} catch {
|
|
10159
10459
|
return out;
|
|
10160
10460
|
}
|
|
10161
|
-
const
|
|
10162
|
-
if (!
|
|
10163
|
-
for (const
|
|
10461
|
+
const pluginsRoot = resolve20(workspaceRoot, "plugins");
|
|
10462
|
+
if (!existsSync16(pluginsRoot)) return out;
|
|
10463
|
+
for (const bundleEntry of readdirSync6(pluginsRoot)) {
|
|
10464
|
+
const bundleDir = resolve20(pluginsRoot, bundleEntry);
|
|
10465
|
+
if (!isDir(bundleDir)) continue;
|
|
10466
|
+
const providersRoot = resolve20(bundleDir, "providers");
|
|
10467
|
+
if (!isDir(providersRoot)) continue;
|
|
10468
|
+
collectBundleProviderScopes(providersRoot, specRoot, out);
|
|
10469
|
+
}
|
|
10470
|
+
return out;
|
|
10471
|
+
}
|
|
10472
|
+
function isDir(path) {
|
|
10473
|
+
try {
|
|
10474
|
+
return existsSync16(path) && statSync4(path).isDirectory();
|
|
10475
|
+
} catch {
|
|
10476
|
+
return false;
|
|
10477
|
+
}
|
|
10478
|
+
}
|
|
10479
|
+
function collectBundleProviderScopes(providersRoot, specRoot, out) {
|
|
10480
|
+
for (const entry of readdirSync6(providersRoot)) {
|
|
10164
10481
|
const providerDir = resolve20(providersRoot, entry);
|
|
10165
|
-
if (!
|
|
10482
|
+
if (!isDir(providerDir)) continue;
|
|
10166
10483
|
const conformanceDir = resolve20(providerDir, "conformance");
|
|
10167
|
-
if (!
|
|
10484
|
+
if (!existsSync16(conformanceDir)) continue;
|
|
10168
10485
|
const casesDir = resolve20(conformanceDir, "cases");
|
|
10169
10486
|
const fixturesDir = resolve20(conformanceDir, "fixtures");
|
|
10170
|
-
if (!
|
|
10487
|
+
if (!existsSync16(casesDir) || !existsSync16(fixturesDir)) continue;
|
|
10171
10488
|
out.push({
|
|
10172
10489
|
id: `provider:${entry}`,
|
|
10173
10490
|
kind: "provider",
|
|
@@ -10177,7 +10494,6 @@ function collectProviderScopes(specRoot) {
|
|
|
10177
10494
|
specRoot
|
|
10178
10495
|
});
|
|
10179
10496
|
}
|
|
10180
|
-
return out;
|
|
10181
10497
|
}
|
|
10182
10498
|
function specScope(specRoot) {
|
|
10183
10499
|
return {
|
|
@@ -10206,8 +10522,8 @@ function selectConformanceScopes(scope) {
|
|
|
10206
10522
|
return [match];
|
|
10207
10523
|
}
|
|
10208
10524
|
function listCaseFiles(scope) {
|
|
10209
|
-
if (!
|
|
10210
|
-
return
|
|
10525
|
+
if (!existsSync16(scope.casesDir)) return [];
|
|
10526
|
+
return readdirSync6(scope.casesDir).filter((entry) => entry.endsWith(".json")).sort().map((entry) => resolve20(scope.casesDir, entry));
|
|
10211
10527
|
}
|
|
10212
10528
|
|
|
10213
10529
|
// cli/commands/conformance.ts
|
|
@@ -10221,12 +10537,12 @@ function formatAssertionFailureDetail(type, reason) {
|
|
|
10221
10537
|
});
|
|
10222
10538
|
}
|
|
10223
10539
|
function resolveBinary() {
|
|
10224
|
-
const here =
|
|
10540
|
+
const here = dirname12(fileURLToPath4(import.meta.url));
|
|
10225
10541
|
let cursor = here;
|
|
10226
10542
|
for (let depth = 0; depth < 6; depth += 1) {
|
|
10227
10543
|
const candidate = resolve21(cursor, "bin", "sm.js");
|
|
10228
|
-
if (
|
|
10229
|
-
const parent =
|
|
10544
|
+
if (existsSync17(candidate)) return candidate;
|
|
10545
|
+
const parent = dirname12(cursor);
|
|
10230
10546
|
if (parent === cursor) break;
|
|
10231
10547
|
cursor = parent;
|
|
10232
10548
|
}
|
|
@@ -10291,7 +10607,7 @@ var ConformanceRunCommand = class extends SmCommand {
|
|
|
10291
10607
|
return ExitCode.Error;
|
|
10292
10608
|
}
|
|
10293
10609
|
const binary = resolveBinary();
|
|
10294
|
-
if (!
|
|
10610
|
+
if (!existsSync17(binary)) {
|
|
10295
10611
|
if (this.json) {
|
|
10296
10612
|
this.#emitJsonError(
|
|
10297
10613
|
"internal",
|
|
@@ -10465,7 +10781,7 @@ function projectAssertionFailures(assertions) {
|
|
|
10465
10781
|
}
|
|
10466
10782
|
function readCaseId(casePath) {
|
|
10467
10783
|
try {
|
|
10468
|
-
const raw =
|
|
10784
|
+
const raw = readFileSync15(casePath, "utf8");
|
|
10469
10785
|
const parsed = JSON.parse(raw);
|
|
10470
10786
|
if (typeof parsed.id === "string") return parsed.id;
|
|
10471
10787
|
} catch {
|
|
@@ -10483,7 +10799,7 @@ function writeStreamSnippet(stream, header, text) {
|
|
|
10483
10799
|
var CONFORMANCE_COMMANDS = [ConformanceRunCommand];
|
|
10484
10800
|
|
|
10485
10801
|
// cli/commands/db/backup.ts
|
|
10486
|
-
import { dirname as
|
|
10802
|
+
import { dirname as dirname13, join as join11, resolve as resolve22 } from "path";
|
|
10487
10803
|
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
10488
10804
|
|
|
10489
10805
|
// cli/i18n/db.texts.ts
|
|
@@ -10569,7 +10885,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
10569
10885
|
const exit = requireDbOrExit(path, this.context.stderr);
|
|
10570
10886
|
if (exit !== null) return exit;
|
|
10571
10887
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
10572
|
-
const outPath = this.out ? resolve22(this.out) :
|
|
10888
|
+
const outPath = this.out ? resolve22(this.out) : join11(dirname13(path), "backups", `${ts}.db`);
|
|
10573
10889
|
await withSqlite({ databasePath: path, autoMigrate: false }, async (storage) => {
|
|
10574
10890
|
storage.migrations.writeBackup(outPath);
|
|
10575
10891
|
});
|
|
@@ -10586,7 +10902,7 @@ var DbBackupCommand = class extends SmCommand {
|
|
|
10586
10902
|
|
|
10587
10903
|
// cli/commands/db/restore.ts
|
|
10588
10904
|
import { chmod, copyFile, mkdir, rm } from "fs/promises";
|
|
10589
|
-
import { dirname as
|
|
10905
|
+
import { dirname as dirname14, resolve as resolve23 } from "path";
|
|
10590
10906
|
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
10591
10907
|
|
|
10592
10908
|
// cli/util/fs.ts
|
|
@@ -10669,7 +10985,7 @@ var DbRestoreCommand = class extends SmCommand {
|
|
|
10669
10985
|
return ExitCode.Error;
|
|
10670
10986
|
}
|
|
10671
10987
|
}
|
|
10672
|
-
await mkdir(
|
|
10988
|
+
await mkdir(dirname14(target), { recursive: true });
|
|
10673
10989
|
await copyFile(sourcePath, target);
|
|
10674
10990
|
await chmodOwnerOnlyBestEffort(target);
|
|
10675
10991
|
for (const sidecar of [`${target}-wal`, `${target}-shm`]) {
|
|
@@ -11018,7 +11334,7 @@ function formatSqlValue(value) {
|
|
|
11018
11334
|
|
|
11019
11335
|
// cli/commands/db/migrate.ts
|
|
11020
11336
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
11021
|
-
import { dirname as
|
|
11337
|
+
import { dirname as dirname15 } from "path";
|
|
11022
11338
|
import { Command as Command12, Option as Option11 } from "clipanion";
|
|
11023
11339
|
|
|
11024
11340
|
// cli/i18n/option-validators.texts.ts
|
|
@@ -11105,7 +11421,7 @@ var DbMigrateCommand = class extends SmCommand {
|
|
|
11105
11421
|
return ExitCode.Error;
|
|
11106
11422
|
}
|
|
11107
11423
|
const path = resolveDbPath({ db: this.db, ...defaultRuntimeContext() });
|
|
11108
|
-
if (path !== ":memory:") await mkdir2(
|
|
11424
|
+
if (path !== ":memory:") await mkdir2(dirname15(path), { recursive: true });
|
|
11109
11425
|
const adapter = createSqliteStorage({
|
|
11110
11426
|
databasePath: path,
|
|
11111
11427
|
autoMigrate: false
|
|
@@ -11758,7 +12074,7 @@ var GraphCommand = class extends SmCommand {
|
|
|
11758
12074
|
};
|
|
11759
12075
|
|
|
11760
12076
|
// cli/commands/help.ts
|
|
11761
|
-
import { readFileSync as
|
|
12077
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
11762
12078
|
import { createRequire as createRequire7 } from "module";
|
|
11763
12079
|
import { resolve as resolve25 } from "path";
|
|
11764
12080
|
import { Command as Command15, Option as Option14 } from "clipanion";
|
|
@@ -11981,7 +12297,7 @@ function resolveSpecVersion() {
|
|
|
11981
12297
|
const req = createRequire7(import.meta.url);
|
|
11982
12298
|
const indexPath = req.resolve("@skill-map/spec/index.json");
|
|
11983
12299
|
const pkgPath = resolve25(indexPath, "..", "package.json");
|
|
11984
|
-
const pkg = JSON.parse(
|
|
12300
|
+
const pkg = JSON.parse(readFileSync16(pkgPath, "utf8"));
|
|
11985
12301
|
return pkg.version;
|
|
11986
12302
|
} catch {
|
|
11987
12303
|
return "unknown";
|
|
@@ -12233,13 +12549,13 @@ function registeredVerbPaths(cli2) {
|
|
|
12233
12549
|
// cli/commands/hooks.ts
|
|
12234
12550
|
import {
|
|
12235
12551
|
chmodSync,
|
|
12236
|
-
existsSync as
|
|
12552
|
+
existsSync as existsSync18,
|
|
12237
12553
|
mkdirSync as mkdirSync5,
|
|
12238
|
-
readFileSync as
|
|
12239
|
-
statSync as
|
|
12554
|
+
readFileSync as readFileSync17,
|
|
12555
|
+
statSync as statSync5,
|
|
12240
12556
|
writeFileSync as writeFileSync2
|
|
12241
12557
|
} from "fs";
|
|
12242
|
-
import { dirname as
|
|
12558
|
+
import { dirname as dirname16, resolve as resolve26 } from "path";
|
|
12243
12559
|
import { Command as Command16, Option as Option15 } from "clipanion";
|
|
12244
12560
|
|
|
12245
12561
|
// cli/i18n/hooks.texts.ts
|
|
@@ -12332,7 +12648,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12332
12648
|
}
|
|
12333
12649
|
const hooksDir = resolve26(repoRoot, ".git", "hooks");
|
|
12334
12650
|
const hookPath = resolve26(hooksDir, "pre-commit");
|
|
12335
|
-
const existing =
|
|
12651
|
+
const existing = existsSync18(hookPath) ? readFileSync17(hookPath, "utf8") : null;
|
|
12336
12652
|
const planned2 = computePlannedHookContent(existing);
|
|
12337
12653
|
if (planned2.kind === "already-installed") {
|
|
12338
12654
|
this.printer.info(tx(HOOKS_TEXTS.alreadyInstalled, { glyph: okGlyph, hookPath }));
|
|
@@ -12358,7 +12674,7 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12358
12674
|
return ExitCode.Ok;
|
|
12359
12675
|
}
|
|
12360
12676
|
try {
|
|
12361
|
-
if (!
|
|
12677
|
+
if (!existsSync18(hooksDir)) mkdirSync5(hooksDir, { recursive: true });
|
|
12362
12678
|
writeFileSync2(hookPath, planned2.content, { encoding: "utf8" });
|
|
12363
12679
|
ensureExecutableBit(hookPath);
|
|
12364
12680
|
} catch (err) {
|
|
@@ -12389,8 +12705,8 @@ var HooksInstallCommand = class extends SmCommand {
|
|
|
12389
12705
|
function findGitRepoRoot(cwd) {
|
|
12390
12706
|
let current = cwd;
|
|
12391
12707
|
while (true) {
|
|
12392
|
-
if (
|
|
12393
|
-
const parent =
|
|
12708
|
+
if (existsSync18(resolve26(current, ".git"))) return current;
|
|
12709
|
+
const parent = dirname16(current);
|
|
12394
12710
|
if (parent === current) return null;
|
|
12395
12711
|
current = parent;
|
|
12396
12712
|
}
|
|
@@ -12404,18 +12720,18 @@ function computePlannedHookContent(existing) {
|
|
|
12404
12720
|
return { kind: "chained", content: existing + sep6 + "\n" + SKILL_MAP_BLOCK };
|
|
12405
12721
|
}
|
|
12406
12722
|
function ensureExecutableBit(path) {
|
|
12407
|
-
const mode =
|
|
12723
|
+
const mode = statSync5(path).mode;
|
|
12408
12724
|
chmodSync(path, mode | 73);
|
|
12409
12725
|
}
|
|
12410
12726
|
var HOOKS_COMMANDS = [HooksInstallCommand];
|
|
12411
12727
|
|
|
12412
12728
|
// cli/commands/init.ts
|
|
12413
12729
|
import { mkdir as mkdir3, readFile as readFile2, writeFile } from "fs/promises";
|
|
12414
|
-
import { join as
|
|
12730
|
+
import { join as join15 } from "path";
|
|
12415
12731
|
import { Command as Command17, Option as Option16 } from "clipanion";
|
|
12416
12732
|
|
|
12417
12733
|
// kernel/orchestrator/index.ts
|
|
12418
|
-
import { existsSync as
|
|
12734
|
+
import { existsSync as existsSync21, statSync as statSync7 } from "fs";
|
|
12419
12735
|
import { Tiktoken as Tiktoken2 } from "js-tiktoken/lite";
|
|
12420
12736
|
import cl100k_base from "js-tiktoken/ranks/cl100k_base";
|
|
12421
12737
|
|
|
@@ -12533,7 +12849,7 @@ async function runExtractorsForNode(opts) {
|
|
|
12533
12849
|
}
|
|
12534
12850
|
function readDeclaredContributions(extension) {
|
|
12535
12851
|
const out = /* @__PURE__ */ new Map();
|
|
12536
|
-
const raw = extension.
|
|
12852
|
+
const raw = extension.ui;
|
|
12537
12853
|
if (typeof raw !== "object" || raw === null) return out;
|
|
12538
12854
|
for (const [id, value] of Object.entries(raw)) {
|
|
12539
12855
|
if (typeof value !== "object" || value === null) continue;
|
|
@@ -12554,11 +12870,13 @@ function emitExtensionError(emitter, qualifiedId2, nodePath, data) {
|
|
|
12554
12870
|
);
|
|
12555
12871
|
}
|
|
12556
12872
|
function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enrichNode, emitContribution, store) {
|
|
12557
|
-
const scope = extractor.scope;
|
|
12873
|
+
const scope = extractor.scope ?? "both";
|
|
12874
|
+
const settings = extractor.resolvedSettings ?? {};
|
|
12558
12875
|
return {
|
|
12559
12876
|
node,
|
|
12560
12877
|
body: scope === "frontmatter" ? "" : body,
|
|
12561
12878
|
frontmatter: scope === "body" ? {} : frontmatter,
|
|
12879
|
+
settings,
|
|
12562
12880
|
emitLink,
|
|
12563
12881
|
enrichNode,
|
|
12564
12882
|
emitContribution,
|
|
@@ -12566,25 +12884,26 @@ function buildExtractorContext(extractor, node, body, frontmatter, emitLink, enr
|
|
|
12566
12884
|
};
|
|
12567
12885
|
}
|
|
12568
12886
|
function validateLink(extractor, link2, emitter) {
|
|
12569
|
-
|
|
12887
|
+
const knownKinds = ["invokes", "references", "mentions", "supersedes"];
|
|
12888
|
+
if (!knownKinds.includes(link2.kind)) {
|
|
12570
12889
|
const qualifiedId2 = `${extractor.pluginId}/${extractor.id}`;
|
|
12571
12890
|
emitter.emit(
|
|
12572
12891
|
makeEvent("extension.error", {
|
|
12573
12892
|
kind: "link-kind-not-declared",
|
|
12574
12893
|
extensionId: qualifiedId2,
|
|
12575
12894
|
linkKind: link2.kind,
|
|
12576
|
-
declaredKinds:
|
|
12895
|
+
declaredKinds: knownKinds,
|
|
12577
12896
|
link: { source: link2.source, target: link2.target, kind: link2.kind },
|
|
12578
12897
|
message: tx(ORCHESTRATOR_TEXTS.extensionErrorLinkKindNotDeclared, {
|
|
12579
12898
|
extractorId: qualifiedId2,
|
|
12580
12899
|
linkKind: link2.kind,
|
|
12581
|
-
declaredKinds:
|
|
12900
|
+
declaredKinds: knownKinds.join(", ")
|
|
12582
12901
|
})
|
|
12583
12902
|
})
|
|
12584
12903
|
);
|
|
12585
12904
|
return null;
|
|
12586
12905
|
}
|
|
12587
|
-
const confidence = link2.confidence ??
|
|
12906
|
+
const confidence = link2.confidence ?? "medium";
|
|
12588
12907
|
return { ...link2, confidence };
|
|
12589
12908
|
}
|
|
12590
12909
|
function dedupeLinks(links) {
|
|
@@ -12642,7 +12961,7 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
12642
12961
|
const issues = [];
|
|
12643
12962
|
const contributions = [];
|
|
12644
12963
|
const validators = loadSchemaValidators();
|
|
12645
|
-
|
|
12964
|
+
void registeredActionIds;
|
|
12646
12965
|
const analyzerOrphans = orphanSidecars.map((o) => ({
|
|
12647
12966
|
relativePath: o.relativePath,
|
|
12648
12967
|
expectedMdPath: o.expectedMdPath
|
|
@@ -12714,27 +13033,6 @@ async function runAnalyzers(analyzers, nodes, internalLinks, orphanSidecars, sid
|
|
|
12714
13033
|
}
|
|
12715
13034
|
return { issues, contributions };
|
|
12716
13035
|
}
|
|
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
13036
|
function validateIssue(analyzer, issue, emitter) {
|
|
12739
13037
|
const severity = issue.severity;
|
|
12740
13038
|
if (severity !== "error" && severity !== "warn" && severity !== "info") {
|
|
@@ -12812,9 +13110,15 @@ function originatingNodeOf(link2, priorNodePaths) {
|
|
|
12812
13110
|
return link2.source;
|
|
12813
13111
|
}
|
|
12814
13112
|
function computeCacheDecision(opts) {
|
|
12815
|
-
const applicableExtractors = opts.extractors.filter(
|
|
12816
|
-
|
|
12817
|
-
|
|
13113
|
+
const applicableExtractors = opts.extractors.filter((ex) => {
|
|
13114
|
+
const kinds = ex.precondition?.kind;
|
|
13115
|
+
if (!kinds || kinds.length === 0) return true;
|
|
13116
|
+
return kinds.some((qualified) => {
|
|
13117
|
+
const slashIdx = qualified.indexOf("/");
|
|
13118
|
+
const kindOnly = slashIdx === -1 ? qualified : qualified.slice(slashIdx + 1);
|
|
13119
|
+
return kindOnly === opts.kind;
|
|
13120
|
+
});
|
|
13121
|
+
});
|
|
12818
13122
|
const applicableQualifiedIds = new Set(
|
|
12819
13123
|
applicableExtractors.map((ex) => qualifiedExtensionId(ex.pluginId, ex.id))
|
|
12820
13124
|
);
|
|
@@ -13005,6 +13309,7 @@ function flagAmbiguousRenames(opts) {
|
|
|
13005
13309
|
function flagOrphans(opts) {
|
|
13006
13310
|
for (const fromPath of opts.deletedPaths) {
|
|
13007
13311
|
if (opts.claimedDeleted.has(fromPath)) continue;
|
|
13312
|
+
if (opts.silenced?.(fromPath)) continue;
|
|
13008
13313
|
opts.issues.push({
|
|
13009
13314
|
analyzerId: "orphan",
|
|
13010
13315
|
severity: "info",
|
|
@@ -13014,7 +13319,7 @@ function flagOrphans(opts) {
|
|
|
13014
13319
|
});
|
|
13015
13320
|
}
|
|
13016
13321
|
}
|
|
13017
|
-
function detectRenamesAndOrphans(prior, current, issues) {
|
|
13322
|
+
function detectRenamesAndOrphans(prior, current, issues, silenced) {
|
|
13018
13323
|
const priorByPath = /* @__PURE__ */ new Map();
|
|
13019
13324
|
for (const n of prior.nodes) priorByPath.set(n.path, n);
|
|
13020
13325
|
const currentByPath = /* @__PURE__ */ new Map();
|
|
@@ -13048,7 +13353,12 @@ function detectRenamesAndOrphans(prior, current, issues) {
|
|
|
13048
13353
|
issues
|
|
13049
13354
|
}));
|
|
13050
13355
|
flagAmbiguousRenames({ newPaths, candidatesByNew, claimedDeleted, claimedNew, issues });
|
|
13051
|
-
flagOrphans({
|
|
13356
|
+
flagOrphans({
|
|
13357
|
+
deletedPaths,
|
|
13358
|
+
claimedDeleted,
|
|
13359
|
+
issues,
|
|
13360
|
+
...silenced ? { silenced } : {}
|
|
13361
|
+
});
|
|
13052
13362
|
return ops;
|
|
13053
13363
|
}
|
|
13054
13364
|
|
|
@@ -13063,8 +13373,8 @@ function computeDriftStatus(args2) {
|
|
|
13063
13373
|
}
|
|
13064
13374
|
|
|
13065
13375
|
// kernel/sidecar/discover-orphans.ts
|
|
13066
|
-
import { existsSync as
|
|
13067
|
-
import { join as
|
|
13376
|
+
import { existsSync as existsSync19, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
13377
|
+
import { join as join12, relative as relative4, sep as sep3 } from "path";
|
|
13068
13378
|
function discoverOrphanSidecars(roots, shouldSkip) {
|
|
13069
13379
|
const out = [];
|
|
13070
13380
|
for (const root of roots) {
|
|
@@ -13075,12 +13385,12 @@ function discoverOrphanSidecars(roots, shouldSkip) {
|
|
|
13075
13385
|
function walk(root, current, shouldSkip, out) {
|
|
13076
13386
|
let entries;
|
|
13077
13387
|
try {
|
|
13078
|
-
entries =
|
|
13388
|
+
entries = readdirSync7(current, { withFileTypes: true, encoding: "utf8" });
|
|
13079
13389
|
} catch {
|
|
13080
13390
|
return;
|
|
13081
13391
|
}
|
|
13082
13392
|
for (const entry of entries) {
|
|
13083
|
-
const full =
|
|
13393
|
+
const full = join12(current, entry.name);
|
|
13084
13394
|
const rel = relative4(root, full).split(sep3).join("/");
|
|
13085
13395
|
if (shouldSkip(rel)) continue;
|
|
13086
13396
|
if (entry.isSymbolicLink()) continue;
|
|
@@ -13091,13 +13401,13 @@ function walk(root, current, shouldSkip, out) {
|
|
|
13091
13401
|
if (!entry.isFile()) continue;
|
|
13092
13402
|
if (!entry.name.endsWith(".sm")) continue;
|
|
13093
13403
|
const expectedMd = `${full.slice(0, -".sm".length)}.md`;
|
|
13094
|
-
if (
|
|
13404
|
+
if (existsSync19(expectedMd) && safeIsFile(expectedMd)) continue;
|
|
13095
13405
|
out.push({ sidecarPath: full, relativePath: rel, expectedMdPath: expectedMd });
|
|
13096
13406
|
}
|
|
13097
13407
|
}
|
|
13098
13408
|
function safeIsFile(path) {
|
|
13099
13409
|
try {
|
|
13100
|
-
return
|
|
13410
|
+
return statSync6(path).isFile();
|
|
13101
13411
|
} catch {
|
|
13102
13412
|
return false;
|
|
13103
13413
|
}
|
|
@@ -13105,7 +13415,7 @@ function safeIsFile(path) {
|
|
|
13105
13415
|
|
|
13106
13416
|
// kernel/orchestrator/node-build.ts
|
|
13107
13417
|
import { createHash } from "crypto";
|
|
13108
|
-
import { existsSync as
|
|
13418
|
+
import { existsSync as existsSync20 } from "fs";
|
|
13109
13419
|
import { isAbsolute as isAbsolute6, resolve as resolvePath } from "path";
|
|
13110
13420
|
import "js-tiktoken/lite";
|
|
13111
13421
|
import yaml4 from "js-yaml";
|
|
@@ -13269,11 +13579,11 @@ function resolveSidecarOverlay(relativePath2, nodePathForIssue, roots, liveBodyH
|
|
|
13269
13579
|
}
|
|
13270
13580
|
function resolveAbsoluteMdPath(relativePath2, roots) {
|
|
13271
13581
|
if (isAbsolute6(relativePath2)) {
|
|
13272
|
-
return
|
|
13582
|
+
return existsSync20(relativePath2) ? relativePath2 : null;
|
|
13273
13583
|
}
|
|
13274
13584
|
for (const root of roots) {
|
|
13275
13585
|
const candidate = resolvePath(root, relativePath2);
|
|
13276
|
-
if (
|
|
13586
|
+
if (existsSync20(candidate)) return candidate;
|
|
13277
13587
|
}
|
|
13278
13588
|
return null;
|
|
13279
13589
|
}
|
|
@@ -13386,6 +13696,9 @@ function buildWalkContext(opts) {
|
|
|
13386
13696
|
async function processRawNode(raw, provider, wctx, accum, claimedPaths, nextIndex) {
|
|
13387
13697
|
const bodyHash = sha256(raw.body);
|
|
13388
13698
|
const frontmatterHash = sha256(canonicalFrontmatter(raw.frontmatter, raw.frontmatterRaw));
|
|
13699
|
+
if (Array.isArray(provider.roots) && provider.roots.length > 0) {
|
|
13700
|
+
if (!matchesAnyRoot(raw.path, provider.roots)) return false;
|
|
13701
|
+
}
|
|
13389
13702
|
const kind = provider.classify(raw.path, raw.frontmatter);
|
|
13390
13703
|
if (kind === null) {
|
|
13391
13704
|
return false;
|
|
@@ -13554,6 +13867,26 @@ function recordExtractorRuns(nodePath, ctx, accum) {
|
|
|
13554
13867
|
});
|
|
13555
13868
|
}
|
|
13556
13869
|
}
|
|
13870
|
+
function matchesAnyRoot(relPath, roots) {
|
|
13871
|
+
for (const r of roots) {
|
|
13872
|
+
if (matchesOneRoot(relPath, r)) return true;
|
|
13873
|
+
}
|
|
13874
|
+
return false;
|
|
13875
|
+
}
|
|
13876
|
+
function matchesOneRoot(relPath, pattern) {
|
|
13877
|
+
if (pattern.endsWith("/**")) return matchesDeepGlob(relPath, pattern.slice(0, -3));
|
|
13878
|
+
if (pattern.endsWith("/*")) return matchesShallowGlob(relPath, pattern.slice(0, -2));
|
|
13879
|
+
return relPath === pattern;
|
|
13880
|
+
}
|
|
13881
|
+
function matchesDeepGlob(relPath, prefix) {
|
|
13882
|
+
if (prefix.length === 0) return true;
|
|
13883
|
+
return relPath === prefix || relPath.startsWith(`${prefix}/`);
|
|
13884
|
+
}
|
|
13885
|
+
function matchesShallowGlob(relPath, prefix) {
|
|
13886
|
+
if (!relPath.startsWith(`${prefix}/`)) return false;
|
|
13887
|
+
const tail = relPath.slice(prefix.length + 1);
|
|
13888
|
+
return tail.length > 0 && !tail.includes("/");
|
|
13889
|
+
}
|
|
13557
13890
|
|
|
13558
13891
|
// kernel/orchestrator/index.ts
|
|
13559
13892
|
var SCANNED_BY = {
|
|
@@ -13622,7 +13955,8 @@ async function runScanInternal(_kernel, options) {
|
|
|
13622
13955
|
mergeAnalyzerEmissions(walked, analyzerResult, exts.analyzers);
|
|
13623
13956
|
const issues = analyzerResult.issues;
|
|
13624
13957
|
for (const issue of walked.frontmatterIssues) issues.push(issue);
|
|
13625
|
-
const
|
|
13958
|
+
const silenced = options.ignoreFilter ? (path) => options.ignoreFilter.ignores(path) : void 0;
|
|
13959
|
+
const renameOps = prior ? detectRenamesAndOrphans(prior, walked.nodes, issues, silenced) : [];
|
|
13626
13960
|
const stats = buildScanStats(walked, issues, start);
|
|
13627
13961
|
const scanCompletedEvent = makeEvent("scan.completed", { stats });
|
|
13628
13962
|
emitter.emit(scanCompletedEvent);
|
|
@@ -13665,7 +13999,7 @@ async function dispatchExtractorCompleted(extractors, emitter, hookDispatcher) {
|
|
|
13665
13999
|
function mergeAnalyzerEmissions(walked, analyzerResult, analyzers) {
|
|
13666
14000
|
for (const c of analyzerResult.contributions) walked.contributions.push(c);
|
|
13667
14001
|
for (const analyzer of analyzers ?? []) {
|
|
13668
|
-
if (analyzer.
|
|
14002
|
+
if (analyzer.ui === void 0) continue;
|
|
13669
14003
|
for (const node of walked.nodes) {
|
|
13670
14004
|
walked.freshlyRunTuples.add(`${analyzer.pluginId}\0${analyzer.id}\0${node.path}`);
|
|
13671
14005
|
}
|
|
@@ -13712,7 +14046,7 @@ function validateRoots(roots) {
|
|
|
13712
14046
|
throw new Error(ORCHESTRATOR_TEXTS.runScanRootEmptyArray);
|
|
13713
14047
|
}
|
|
13714
14048
|
for (const root of roots) {
|
|
13715
|
-
if (!
|
|
14049
|
+
if (!existsSync21(root) || !statSync7(root).isDirectory()) {
|
|
13716
14050
|
throw new Error(tx(ORCHESTRATOR_TEXTS.runScanRootMissing, { root }));
|
|
13717
14051
|
}
|
|
13718
14052
|
}
|
|
@@ -13940,16 +14274,16 @@ function createKernel() {
|
|
|
13940
14274
|
}
|
|
13941
14275
|
|
|
13942
14276
|
// kernel/jobs/orphan-files.ts
|
|
13943
|
-
import { readdirSync as
|
|
13944
|
-
import { join as
|
|
14277
|
+
import { readdirSync as readdirSync8, statSync as statSync8 } from "fs";
|
|
14278
|
+
import { join as join13, resolve as resolve28 } from "path";
|
|
13945
14279
|
function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
13946
14280
|
let entries;
|
|
13947
14281
|
try {
|
|
13948
|
-
const stat2 =
|
|
14282
|
+
const stat2 = statSync8(jobsDir);
|
|
13949
14283
|
if (!stat2.isDirectory()) {
|
|
13950
14284
|
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
13951
14285
|
}
|
|
13952
|
-
entries =
|
|
14286
|
+
entries = readdirSync8(jobsDir, { withFileTypes: true });
|
|
13953
14287
|
} catch {
|
|
13954
14288
|
return { orphanFilePaths: [], referencedCount: referencedPaths.size };
|
|
13955
14289
|
}
|
|
@@ -13959,7 +14293,7 @@ function findOrphanJobFiles(jobsDir, referencedPaths) {
|
|
|
13959
14293
|
if (!entry.isFile()) continue;
|
|
13960
14294
|
const name = entry.name;
|
|
13961
14295
|
if (!name.endsWith(".md")) continue;
|
|
13962
|
-
const abs = resolve28(
|
|
14296
|
+
const abs = resolve28(join13(jobsDir, name));
|
|
13963
14297
|
if (!referencedPaths.has(abs)) orphans.push(abs);
|
|
13964
14298
|
}
|
|
13965
14299
|
orphans.sort();
|
|
@@ -14033,9 +14367,9 @@ var SCAN_RUNNER_TEXTS = {
|
|
|
14033
14367
|
};
|
|
14034
14368
|
|
|
14035
14369
|
// core/runtime/reference-paths-walker.ts
|
|
14036
|
-
import { readdirSync as
|
|
14370
|
+
import { readdirSync as readdirSync9, statSync as statSync9 } from "fs";
|
|
14037
14371
|
import { homedir as osHomedir2 } from "os";
|
|
14038
|
-
import { isAbsolute as isAbsolute7, join as
|
|
14372
|
+
import { isAbsolute as isAbsolute7, join as join14, resolve as resolve29 } from "path";
|
|
14039
14373
|
var REFERENCE_WALK_MAX_FILES = 5e4;
|
|
14040
14374
|
var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
14041
14375
|
"node_modules",
|
|
@@ -14043,7 +14377,7 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
|
14043
14377
|
SKILL_MAP_DIR
|
|
14044
14378
|
]);
|
|
14045
14379
|
function resolveScanPath(raw, cwd) {
|
|
14046
|
-
if (raw.startsWith("~/")) return resolve29(
|
|
14380
|
+
if (raw.startsWith("~/")) return resolve29(join14(osHomedir2(), raw.slice(2)));
|
|
14047
14381
|
if (raw === "~") return resolve29(osHomedir2());
|
|
14048
14382
|
if (isAbsolute7(raw)) return resolve29(raw);
|
|
14049
14383
|
return resolve29(cwd, raw);
|
|
@@ -14068,14 +14402,14 @@ function walkInto(dir, out) {
|
|
|
14068
14402
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
14069
14403
|
let entries;
|
|
14070
14404
|
try {
|
|
14071
|
-
entries =
|
|
14405
|
+
entries = readdirSync9(dir, { withFileTypes: true });
|
|
14072
14406
|
} catch {
|
|
14073
14407
|
return false;
|
|
14074
14408
|
}
|
|
14075
14409
|
for (const entry of entries) {
|
|
14076
14410
|
if (out.size >= REFERENCE_WALK_MAX_FILES) return true;
|
|
14077
14411
|
if (entry.isSymbolicLink()) continue;
|
|
14078
|
-
const full =
|
|
14412
|
+
const full = join14(dir, entry.name);
|
|
14079
14413
|
if (entry.isDirectory()) {
|
|
14080
14414
|
if (SKIPPED_DIR_NAMES.has(entry.name)) continue;
|
|
14081
14415
|
if (walkInto(full, out)) return true;
|
|
@@ -14087,7 +14421,7 @@ function walkInto(dir, out) {
|
|
|
14087
14421
|
}
|
|
14088
14422
|
function safeStat(path) {
|
|
14089
14423
|
try {
|
|
14090
|
-
return
|
|
14424
|
+
return statSync9(path);
|
|
14091
14425
|
} catch {
|
|
14092
14426
|
return null;
|
|
14093
14427
|
}
|
|
@@ -14399,7 +14733,7 @@ var InitCommand = class extends SmCommand {
|
|
|
14399
14733
|
async run() {
|
|
14400
14734
|
const ctx = defaultRuntimeContext();
|
|
14401
14735
|
const scopeRoot = ctx.cwd;
|
|
14402
|
-
const skillMapDir =
|
|
14736
|
+
const skillMapDir = join15(scopeRoot, SKILL_MAP_DIR);
|
|
14403
14737
|
const settingsPath = defaultSettingsPath(scopeRoot);
|
|
14404
14738
|
const localPath = defaultLocalSettingsPath(scopeRoot);
|
|
14405
14739
|
const ignorePath = defaultIgnoreFilePath(scopeRoot);
|
|
@@ -14439,13 +14773,13 @@ var InitCommand = class extends SmCommand {
|
|
|
14439
14773
|
writeFileAtomicExclusive(localPath, "{}\n");
|
|
14440
14774
|
}
|
|
14441
14775
|
if (!await pathExists(ignorePath) || this.force) {
|
|
14442
|
-
writeFileAtomicExclusive(ignorePath, loadBundledIgnoreText());
|
|
14776
|
+
writeFileAtomicExclusive(ignorePath, loadBundledIgnoreText(), 420);
|
|
14443
14777
|
}
|
|
14444
14778
|
const ansi = this.ansiFor("stdout");
|
|
14445
14779
|
const okGlyph = ansi.green("\u2713");
|
|
14446
14780
|
const updated = await ensureGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
14447
14781
|
if (updated) {
|
|
14448
|
-
const gitignorePath =
|
|
14782
|
+
const gitignorePath = join15(scopeRoot, ".gitignore");
|
|
14449
14783
|
printer.info(
|
|
14450
14784
|
GITIGNORE_ENTRIES.length === 1 ? tx(INIT_TEXTS.gitignoreUpdatedSingular, { glyph: okGlyph, path: gitignorePath }) : tx(INIT_TEXTS.gitignoreUpdatedPlural, {
|
|
14451
14785
|
glyph: okGlyph,
|
|
@@ -14484,7 +14818,7 @@ async function dryRunFileMessage(path) {
|
|
|
14484
14818
|
}
|
|
14485
14819
|
async function writeDryRunGitignorePlan(printer, scopeRoot) {
|
|
14486
14820
|
const wouldAdd = await previewGitignoreEntries(scopeRoot, GITIGNORE_ENTRIES);
|
|
14487
|
-
const gitignorePath =
|
|
14821
|
+
const gitignorePath = join15(scopeRoot, ".gitignore");
|
|
14488
14822
|
if (wouldAdd.length === 0) {
|
|
14489
14823
|
printer.info(tx(INIT_TEXTS.dryRunWouldLeaveGitignoreUnchanged, { path: gitignorePath }));
|
|
14490
14824
|
} else if (wouldAdd.length === 1) {
|
|
@@ -14559,7 +14893,7 @@ async function runFirstScan(scopeRoot, strict, printer, stderr, ansi) {
|
|
|
14559
14893
|
return hasErrors ? ExitCode.Issues : ExitCode.Ok;
|
|
14560
14894
|
}
|
|
14561
14895
|
async function previewGitignoreEntries(scopeRoot, entries) {
|
|
14562
|
-
const path =
|
|
14896
|
+
const path = join15(scopeRoot, ".gitignore");
|
|
14563
14897
|
const body = await pathExists(path) ? await readFile2(path, "utf8") : "";
|
|
14564
14898
|
const present = new Set(
|
|
14565
14899
|
body.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"))
|
|
@@ -14567,7 +14901,7 @@ async function previewGitignoreEntries(scopeRoot, entries) {
|
|
|
14567
14901
|
return entries.filter((entry) => !present.has(entry));
|
|
14568
14902
|
}
|
|
14569
14903
|
async function ensureGitignoreEntries(scopeRoot, entries) {
|
|
14570
|
-
const path =
|
|
14904
|
+
const path = join15(scopeRoot, ".gitignore");
|
|
14571
14905
|
let body = "";
|
|
14572
14906
|
if (await pathExists(path)) {
|
|
14573
14907
|
body = await readFile2(path, "utf8");
|
|
@@ -16222,7 +16556,7 @@ var PLUGINS_TEXTS = {
|
|
|
16222
16556
|
* Success block printed after scaffolding. Follows the no-em-dash rule
|
|
16223
16557
|
* across every line.
|
|
16224
16558
|
*/
|
|
16225
|
-
createSuccess: "Created {{targetDir}}\nNext:\n - Edit {{pluginId}}/
|
|
16559
|
+
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
16560
|
// --- slots list verb -------------------------------------------------
|
|
16227
16561
|
/** Section header for the view-slots catalogue. */
|
|
16228
16562
|
slotsListHeaderViewSlots: " View slots ({{count}})\n",
|
|
@@ -16282,11 +16616,9 @@ function extensionRowFromBuiltIn(ext, bundle, bundleEnabled, resolveEnabled) {
|
|
|
16282
16616
|
id: ext.id,
|
|
16283
16617
|
kind: ext.kind,
|
|
16284
16618
|
version: ext.version,
|
|
16285
|
-
enabled: bundle.granularity === "bundle" ? bundleEnabled : resolveEnabled(qualifiedExtensionId(bundle.id, ext.id))
|
|
16619
|
+
enabled: bundle.granularity === "bundle" ? bundleEnabled : resolveEnabled(qualifiedExtensionId(bundle.id, ext.id)),
|
|
16620
|
+
description: ext.description ?? ""
|
|
16286
16621
|
};
|
|
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
16622
|
if (ext.entry !== void 0) row.entry = ext.entry;
|
|
16291
16623
|
return row;
|
|
16292
16624
|
}
|
|
@@ -16688,9 +17020,7 @@ function renderBuiltInExtensionDetail(bundleId, ext, ansi) {
|
|
|
16688
17020
|
source: ansi.dim(PLUGINS_TEXTS.sourceBuiltIn)
|
|
16689
17021
|
});
|
|
16690
17022
|
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;
|
|
17023
|
+
if (ext.description) meta.description = ext.description;
|
|
16694
17024
|
if (ext.entry !== void 0) meta.entry = ext.entry;
|
|
16695
17025
|
return header + "\n" + renderExtensionFields(meta);
|
|
16696
17026
|
}
|
|
@@ -16991,11 +17321,12 @@ function collectBuiltInApplicableKindWarnings(out, knownKinds) {
|
|
|
16991
17321
|
for (const ext of bundle.extensions) {
|
|
16992
17322
|
if (ext.kind !== "extractor") continue;
|
|
16993
17323
|
const extractor = ext;
|
|
16994
|
-
|
|
17324
|
+
const kinds = extractor.precondition?.kind;
|
|
17325
|
+
if (!kinds || kinds.length === 0) continue;
|
|
16995
17326
|
appendUnknownKindWarnings(
|
|
16996
17327
|
out,
|
|
16997
17328
|
qualifiedExtensionId(bundle.id, extractor.id),
|
|
16998
|
-
|
|
17329
|
+
kinds,
|
|
16999
17330
|
knownKinds
|
|
17000
17331
|
);
|
|
17001
17332
|
}
|
|
@@ -17005,19 +17336,24 @@ function collectUserApplicableKindWarnings(out, plugins, knownKinds) {
|
|
|
17005
17336
|
for (const p of plugins) {
|
|
17006
17337
|
if (p.status !== "enabled" || !p.extensions) continue;
|
|
17007
17338
|
for (const ext of p.extensions) {
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17339
|
+
collectKindsFromExtension(ext, knownKinds, out);
|
|
17340
|
+
}
|
|
17341
|
+
}
|
|
17342
|
+
}
|
|
17343
|
+
function collectKindsFromExtension(ext, knownKinds, out) {
|
|
17344
|
+
if (ext.kind !== "extractor") return;
|
|
17345
|
+
const inst = extensionInstance(ext);
|
|
17346
|
+
if (!inst) return;
|
|
17347
|
+
const pre = inst["precondition"];
|
|
17348
|
+
if (!pre || typeof pre !== "object") return;
|
|
17349
|
+
const kinds = pre.kind;
|
|
17350
|
+
if (!Array.isArray(kinds)) return;
|
|
17351
|
+
appendUnknownKindWarnings(
|
|
17352
|
+
out,
|
|
17353
|
+
qualifiedExtensionId(ext.pluginId, ext.id),
|
|
17354
|
+
kinds,
|
|
17355
|
+
knownKinds
|
|
17356
|
+
);
|
|
17021
17357
|
}
|
|
17022
17358
|
function appendUnknownKindWarnings(out, extractorQualifiedId, applicableKinds, knownKinds) {
|
|
17023
17359
|
for (const k of applicableKinds) {
|
|
@@ -17370,8 +17706,8 @@ function resolveBareToggle(id, catalogue, verb, ansi) {
|
|
|
17370
17706
|
}
|
|
17371
17707
|
|
|
17372
17708
|
// cli/commands/plugins/create.ts
|
|
17373
|
-
import { existsSync as
|
|
17374
|
-
import { join as
|
|
17709
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
17710
|
+
import { join as join16, resolve as resolve31 } from "path";
|
|
17375
17711
|
import { Command as Command26, Option as Option25 } from "clipanion";
|
|
17376
17712
|
var PluginsCreateCommand = class extends SmCommand {
|
|
17377
17713
|
static paths = [["plugins", "create"]];
|
|
@@ -17397,8 +17733,8 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17397
17733
|
}
|
|
17398
17734
|
const ctx = defaultRuntimeContext();
|
|
17399
17735
|
const baseDir = defaultProjectPluginsDir(ctx);
|
|
17400
|
-
const targetDir = this.at ? resolve31(this.at) :
|
|
17401
|
-
if (
|
|
17736
|
+
const targetDir = this.at ? resolve31(this.at) : join16(baseDir, this.pluginId);
|
|
17737
|
+
if (existsSync22(targetDir) && !this.force) {
|
|
17402
17738
|
this.printer.error(
|
|
17403
17739
|
tx(PLUGINS_TEXTS.createRefuseOverwrite, {
|
|
17404
17740
|
glyph: errGlyph,
|
|
@@ -17407,14 +17743,15 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17407
17743
|
);
|
|
17408
17744
|
return ExitCode.Error;
|
|
17409
17745
|
}
|
|
17410
|
-
|
|
17746
|
+
const extractorName = `${this.pluginId}-extractor`;
|
|
17747
|
+
mkdirSync6(join16(targetDir, "extractors", extractorName), { recursive: true });
|
|
17411
17748
|
const specVersion = installedSpecVersion();
|
|
17412
17749
|
const manifest = {
|
|
17413
17750
|
id: this.pluginId,
|
|
17414
17751
|
version: "0.1.0",
|
|
17415
17752
|
specCompat: `^${specVersion}`,
|
|
17416
17753
|
catalogCompat: "^1.0.0",
|
|
17417
|
-
|
|
17754
|
+
granularity: "bundle",
|
|
17418
17755
|
description: "Generated by `sm plugins create`. Edit to taste.",
|
|
17419
17756
|
settings: {
|
|
17420
17757
|
keywords: {
|
|
@@ -17427,14 +17764,14 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17427
17764
|
}
|
|
17428
17765
|
};
|
|
17429
17766
|
writeFileSync3(
|
|
17430
|
-
|
|
17767
|
+
join16(targetDir, "plugin.json"),
|
|
17431
17768
|
JSON.stringify(manifest, null, 2) + "\n"
|
|
17432
17769
|
);
|
|
17433
17770
|
writeFileSync3(
|
|
17434
|
-
|
|
17435
|
-
scaffolderExtractorStub(
|
|
17771
|
+
join16(targetDir, "extractors", extractorName, "index.js"),
|
|
17772
|
+
scaffolderExtractorStub(extractorName)
|
|
17436
17773
|
);
|
|
17437
|
-
writeFileSync3(
|
|
17774
|
+
writeFileSync3(join16(targetDir, "README.md"), scaffolderReadme(this.pluginId));
|
|
17438
17775
|
this.printer.data(
|
|
17439
17776
|
tx(PLUGINS_TEXTS.createSuccess, {
|
|
17440
17777
|
targetDir: sanitizeForTerminal(targetDir),
|
|
@@ -17444,7 +17781,7 @@ var PluginsCreateCommand = class extends SmCommand {
|
|
|
17444
17781
|
return ExitCode.Ok;
|
|
17445
17782
|
}
|
|
17446
17783
|
};
|
|
17447
|
-
function scaffolderExtractorStub(
|
|
17784
|
+
function scaffolderExtractorStub(extractorId) {
|
|
17448
17785
|
return `/**
|
|
17449
17786
|
* Generated by \`sm plugins create\`. Edit the extract() body.
|
|
17450
17787
|
*
|
|
@@ -17453,6 +17790,11 @@ function scaffolderExtractorStub(pluginId) {
|
|
|
17453
17790
|
* splitting into a named export will surface as \`load-error: default
|
|
17454
17791
|
* export missing a string \\\`kind\\\` field\`.
|
|
17455
17792
|
*
|
|
17793
|
+
* Folder convention: this file lives at
|
|
17794
|
+
* \`extractors/${extractorId}/index.js\`. The bundle's plugin.json#/id
|
|
17795
|
+
* provides the qualified id \`<plugin-id>/${extractorId}\`; the loader
|
|
17796
|
+
* injects \`pluginId\` automatically, do NOT hardcode it here.
|
|
17797
|
+
*
|
|
17456
17798
|
* Declared view contributions (in plugin.json):
|
|
17457
17799
|
* - 'count' \u2192 slot \`card.footer.left\` (renders as a chip
|
|
17458
17800
|
* in the left footer of the node card)
|
|
@@ -17464,13 +17806,11 @@ function scaffolderExtractorStub(pluginId) {
|
|
|
17464
17806
|
* spec/view-slots.md
|
|
17465
17807
|
*/
|
|
17466
17808
|
export default {
|
|
17467
|
-
id: '${
|
|
17468
|
-
pluginId: '${pluginId}',
|
|
17809
|
+
id: '${extractorId}',
|
|
17469
17810
|
kind: 'extractor',
|
|
17470
17811
|
version: '0.1.0',
|
|
17471
17812
|
description: 'Counts configured keywords per node.',
|
|
17472
17813
|
stability: 'experimental',
|
|
17473
|
-
mode: 'deterministic',
|
|
17474
17814
|
emitsLinkKinds: [],
|
|
17475
17815
|
defaultConfidence: 'high',
|
|
17476
17816
|
scope: 'body',
|
|
@@ -17937,9 +18277,15 @@ var RefreshCommand = class extends SmCommand {
|
|
|
17937
18277
|
continue;
|
|
17938
18278
|
}
|
|
17939
18279
|
const fm = node.frontmatter ?? {};
|
|
17940
|
-
const applicable = allExtractors.filter(
|
|
17941
|
-
|
|
17942
|
-
|
|
18280
|
+
const applicable = allExtractors.filter((ex) => {
|
|
18281
|
+
const kinds = ex.precondition?.kind;
|
|
18282
|
+
if (!kinds || kinds.length === 0) return true;
|
|
18283
|
+
return kinds.some((qualified) => {
|
|
18284
|
+
const slashIdx = qualified.indexOf("/");
|
|
18285
|
+
const kindOnly = slashIdx === -1 ? qualified : qualified.slice(slashIdx + 1);
|
|
18286
|
+
return kindOnly === node.kind;
|
|
18287
|
+
});
|
|
18288
|
+
});
|
|
17943
18289
|
for (const extractor of applicable) {
|
|
17944
18290
|
const records = await runExtractorForEnrichment(extractor, node, body, fm);
|
|
17945
18291
|
for (const record of records) nodeEnrichments.push(record);
|
|
@@ -18049,7 +18395,7 @@ var SCAN_TEXTS = {
|
|
|
18049
18395
|
import { Command as Command30, Option as Option28 } from "clipanion";
|
|
18050
18396
|
|
|
18051
18397
|
// core/watcher/runtime.ts
|
|
18052
|
-
import { dirname as
|
|
18398
|
+
import { dirname as dirname17 } from "path";
|
|
18053
18399
|
|
|
18054
18400
|
// core/runtime/fresh-resolver.ts
|
|
18055
18401
|
async function buildFreshResolver(deps) {
|
|
@@ -18274,7 +18620,7 @@ function createWatcherRuntime(opts) {
|
|
|
18274
18620
|
roots: [
|
|
18275
18621
|
cwd,
|
|
18276
18622
|
// parent of `.skillmapignore`
|
|
18277
|
-
|
|
18623
|
+
dirname17(settingsPath)
|
|
18278
18624
|
// parent of `.skill-map/settings.json`
|
|
18279
18625
|
],
|
|
18280
18626
|
cwd,
|
|
@@ -18830,7 +19176,7 @@ function plural(count, word) {
|
|
|
18830
19176
|
}
|
|
18831
19177
|
|
|
18832
19178
|
// cli/commands/scan-compare.ts
|
|
18833
|
-
import { existsSync as
|
|
19179
|
+
import { existsSync as existsSync23, readFileSync as readFileSync18 } from "fs";
|
|
18834
19180
|
import { Command as Command32, Option as Option30 } from "clipanion";
|
|
18835
19181
|
var ScanCompareCommand = class extends SmCommand {
|
|
18836
19182
|
static paths = [["scan", "compare-with"]];
|
|
@@ -18942,12 +19288,12 @@ var ScanCompareCommand = class extends SmCommand {
|
|
|
18942
19288
|
}
|
|
18943
19289
|
};
|
|
18944
19290
|
function loadAndValidateDump(path) {
|
|
18945
|
-
if (!
|
|
19291
|
+
if (!existsSync23(path)) {
|
|
18946
19292
|
throw new Error(tx(SCAN_TEXTS.compareDumpNotFound, { path }));
|
|
18947
19293
|
}
|
|
18948
19294
|
let raw;
|
|
18949
19295
|
try {
|
|
18950
|
-
raw =
|
|
19296
|
+
raw = readFileSync18(path, "utf8");
|
|
18951
19297
|
} catch (err) {
|
|
18952
19298
|
const message = formatErrorMessage(err);
|
|
18953
19299
|
throw new Error(tx(SCAN_TEXTS.compareDumpReadFailed, { path, message }), { cause: err });
|
|
@@ -19072,7 +19418,7 @@ function renderDeltaIssues(issues) {
|
|
|
19072
19418
|
|
|
19073
19419
|
// cli/commands/serve.ts
|
|
19074
19420
|
import { spawn as spawn2 } from "child_process";
|
|
19075
|
-
import { existsSync as
|
|
19421
|
+
import { existsSync as existsSync27 } from "fs";
|
|
19076
19422
|
import { Command as Command33, Option as Option31 } from "clipanion";
|
|
19077
19423
|
|
|
19078
19424
|
// cli/util/browser-launch.ts
|
|
@@ -19725,7 +20071,7 @@ function contentTypeFor(format) {
|
|
|
19725
20071
|
}
|
|
19726
20072
|
|
|
19727
20073
|
// server/health.ts
|
|
19728
|
-
import { existsSync as
|
|
20074
|
+
import { existsSync as existsSync24 } from "fs";
|
|
19729
20075
|
var FALLBACK_SCHEMA_VERSION = "1";
|
|
19730
20076
|
function buildHealth(deps) {
|
|
19731
20077
|
return {
|
|
@@ -19733,7 +20079,7 @@ function buildHealth(deps) {
|
|
|
19733
20079
|
schemaVersion: FALLBACK_SCHEMA_VERSION,
|
|
19734
20080
|
specVersion: deps.specVersion,
|
|
19735
20081
|
implVersion: VERSION,
|
|
19736
|
-
db:
|
|
20082
|
+
db: existsSync24(deps.dbPath) ? "present" : "missing",
|
|
19737
20083
|
cwd: deps.cwd,
|
|
19738
20084
|
dbPath: deps.dbPath
|
|
19739
20085
|
};
|
|
@@ -21097,7 +21443,8 @@ function invokeBump2(node, absPath, body) {
|
|
|
21097
21443
|
node,
|
|
21098
21444
|
nodeAbsolutePath: absPath,
|
|
21099
21445
|
invoker: "ui",
|
|
21100
|
-
now: () => /* @__PURE__ */ new Date()
|
|
21446
|
+
now: () => /* @__PURE__ */ new Date(),
|
|
21447
|
+
settings: {}
|
|
21101
21448
|
});
|
|
21102
21449
|
}
|
|
21103
21450
|
function pickExistingVersion(node) {
|
|
@@ -21134,9 +21481,9 @@ function registerUpdateStatusRoute(app, deps) {
|
|
|
21134
21481
|
}
|
|
21135
21482
|
|
|
21136
21483
|
// server/static.ts
|
|
21137
|
-
import { existsSync as
|
|
21484
|
+
import { existsSync as existsSync25 } from "fs";
|
|
21138
21485
|
import { readFile as readFile5 } from "fs/promises";
|
|
21139
|
-
import { extname, join as
|
|
21486
|
+
import { extname, join as join17 } from "path";
|
|
21140
21487
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
21141
21488
|
var INDEX_HTML = "index.html";
|
|
21142
21489
|
var PLACEHOLDER_HTML = `<!doctype html>
|
|
@@ -21188,8 +21535,8 @@ function createSpaFallback(opts) {
|
|
|
21188
21535
|
return async (c, _next) => {
|
|
21189
21536
|
if (c.req.method !== "GET" && c.req.method !== "HEAD") return c.notFound();
|
|
21190
21537
|
if (opts.uiDist === null) return htmlResponse(c, placeholder);
|
|
21191
|
-
const indexPath =
|
|
21192
|
-
if (!
|
|
21538
|
+
const indexPath = join17(opts.uiDist, INDEX_HTML);
|
|
21539
|
+
if (!existsSync25(indexPath)) return htmlResponse(c, placeholder);
|
|
21193
21540
|
return fileResponse(c, indexPath);
|
|
21194
21541
|
};
|
|
21195
21542
|
}
|
|
@@ -21758,10 +22105,10 @@ function validateNoUi(noUi, uiDist) {
|
|
|
21758
22105
|
}
|
|
21759
22106
|
|
|
21760
22107
|
// server/paths.ts
|
|
21761
|
-
import { existsSync as
|
|
21762
|
-
import { dirname as
|
|
22108
|
+
import { existsSync as existsSync26, statSync as statSync10 } from "fs";
|
|
22109
|
+
import { dirname as dirname18, isAbsolute as isAbsolute9, join as join18, resolve as resolve34 } from "path";
|
|
21763
22110
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
21764
|
-
var DEFAULT_UI_REL =
|
|
22111
|
+
var DEFAULT_UI_REL = join18("ui", "dist", "ui", "browser");
|
|
21765
22112
|
var PACKAGE_UI_REL = "ui";
|
|
21766
22113
|
var INDEX_HTML2 = "index.html";
|
|
21767
22114
|
function resolveDefaultUiDist(ctx) {
|
|
@@ -21773,10 +22120,10 @@ function resolveExplicitUiDist(ctx, raw) {
|
|
|
21773
22120
|
return isAbsolute9(raw) ? raw : resolve34(ctx.cwd, raw);
|
|
21774
22121
|
}
|
|
21775
22122
|
function isUiBundleDir(path) {
|
|
21776
|
-
if (!
|
|
22123
|
+
if (!existsSync26(path)) return false;
|
|
21777
22124
|
try {
|
|
21778
|
-
if (!
|
|
21779
|
-
return
|
|
22125
|
+
if (!statSync10(path).isDirectory()) return false;
|
|
22126
|
+
return existsSync26(join18(path, INDEX_HTML2));
|
|
21780
22127
|
} catch {
|
|
21781
22128
|
return false;
|
|
21782
22129
|
}
|
|
@@ -21784,7 +22131,7 @@ function isUiBundleDir(path) {
|
|
|
21784
22131
|
function resolvePackageBundledUi() {
|
|
21785
22132
|
let here;
|
|
21786
22133
|
try {
|
|
21787
|
-
here =
|
|
22134
|
+
here = dirname18(fileURLToPath5(import.meta.url));
|
|
21788
22135
|
} catch {
|
|
21789
22136
|
return null;
|
|
21790
22137
|
}
|
|
@@ -21793,11 +22140,11 @@ function resolvePackageBundledUi() {
|
|
|
21793
22140
|
function resolvePackageBundledUiFrom(here) {
|
|
21794
22141
|
let current = here;
|
|
21795
22142
|
for (let i = 0; i < 8; i++) {
|
|
21796
|
-
const candidate =
|
|
22143
|
+
const candidate = join18(current, PACKAGE_UI_REL);
|
|
21797
22144
|
if (isUiBundleDir(candidate)) return candidate;
|
|
21798
|
-
const distHere =
|
|
22145
|
+
const distHere = join18(current, "dist", PACKAGE_UI_REL);
|
|
21799
22146
|
if (isUiBundleDir(distHere)) return distHere;
|
|
21800
|
-
const parent =
|
|
22147
|
+
const parent = dirname18(current);
|
|
21801
22148
|
if (parent === current) return null;
|
|
21802
22149
|
current = parent;
|
|
21803
22150
|
}
|
|
@@ -21806,9 +22153,9 @@ function resolvePackageBundledUiFrom(here) {
|
|
|
21806
22153
|
function walkUpForUi(startDir) {
|
|
21807
22154
|
let current = resolve34(startDir);
|
|
21808
22155
|
for (let i = 0; i < 64; i++) {
|
|
21809
|
-
const candidate =
|
|
22156
|
+
const candidate = join18(current, DEFAULT_UI_REL);
|
|
21810
22157
|
if (isUiBundleDir(candidate)) return candidate;
|
|
21811
|
-
const parent =
|
|
22158
|
+
const parent = dirname18(current);
|
|
21812
22159
|
if (parent === current) return null;
|
|
21813
22160
|
current = parent;
|
|
21814
22161
|
}
|
|
@@ -22224,7 +22571,7 @@ var ServeCommand = class extends SmCommand {
|
|
|
22224
22571
|
return ExitCode.Error;
|
|
22225
22572
|
}
|
|
22226
22573
|
const dbPath = resolveDbPath({ db: this.db, ...runtimeCtx });
|
|
22227
|
-
if (this.db !== void 0 && !
|
|
22574
|
+
if (this.db !== void 0 && !existsSync27(dbPath)) {
|
|
22228
22575
|
this.printer.info(
|
|
22229
22576
|
tx(SERVE_TEXTS.dbNotFound, { path: sanitizeForTerminal(dbPath) })
|
|
22230
22577
|
);
|
|
@@ -22720,7 +23067,7 @@ function rankConfidenceForGrouping(c) {
|
|
|
22720
23067
|
}
|
|
22721
23068
|
|
|
22722
23069
|
// cli/commands/sidecar.ts
|
|
22723
|
-
import { existsSync as
|
|
23070
|
+
import { existsSync as existsSync28, unlinkSync as unlinkSync2 } from "fs";
|
|
22724
23071
|
import { resolve as resolve35 } from "path";
|
|
22725
23072
|
import { Command as Command35, Option as Option33 } from "clipanion";
|
|
22726
23073
|
|
|
@@ -23161,7 +23508,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
23161
23508
|
return ExitCode.Error;
|
|
23162
23509
|
}
|
|
23163
23510
|
const sidecarAbsPath = sidecarPathFor(absPath);
|
|
23164
|
-
if (
|
|
23511
|
+
if (existsSync28(sidecarAbsPath) && this.force !== true) {
|
|
23165
23512
|
this.printer.error(
|
|
23166
23513
|
tx(SIDECAR_TEXTS.annotateExists, {
|
|
23167
23514
|
glyph: errGlyph,
|
|
@@ -23171,7 +23518,7 @@ var SidecarAnnotateCommand = class extends SmCommand {
|
|
|
23171
23518
|
);
|
|
23172
23519
|
return ExitCode.Error;
|
|
23173
23520
|
}
|
|
23174
|
-
if (
|
|
23521
|
+
if (existsSync28(sidecarAbsPath) && this.force === true) {
|
|
23175
23522
|
try {
|
|
23176
23523
|
unlinkSync2(sidecarAbsPath);
|
|
23177
23524
|
} catch (err) {
|
|
@@ -23401,9 +23748,9 @@ var STUB_COMMANDS = [
|
|
|
23401
23748
|
];
|
|
23402
23749
|
|
|
23403
23750
|
// cli/commands/tutorial.ts
|
|
23404
|
-
import { existsSync as
|
|
23751
|
+
import { existsSync as existsSync29, readFileSync as readFileSync19 } from "fs";
|
|
23405
23752
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
23406
|
-
import { dirname as
|
|
23753
|
+
import { dirname as dirname19, join as join19, resolve as resolve36 } from "path";
|
|
23407
23754
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
23408
23755
|
import { Command as Command37, Option as Option35 } from "clipanion";
|
|
23409
23756
|
|
|
@@ -23500,7 +23847,7 @@ var TutorialCommand = class extends SmCommand {
|
|
|
23500
23847
|
}
|
|
23501
23848
|
const variant = rawVariant ?? DEFAULT_VARIANT;
|
|
23502
23849
|
const spec = VARIANT_SPECS[variant];
|
|
23503
|
-
const target =
|
|
23850
|
+
const target = join19(ctx.cwd, spec.filename);
|
|
23504
23851
|
if (await pathExists(target) && !this.force) {
|
|
23505
23852
|
this.printer.error(
|
|
23506
23853
|
tx(TUTORIAL_TEXTS.alreadyExists, {
|
|
@@ -23574,7 +23921,7 @@ function loadBundledTutorialText(variant) {
|
|
|
23574
23921
|
}
|
|
23575
23922
|
function readTutorialFromDisk(variant) {
|
|
23576
23923
|
const spec = VARIANT_SPECS[variant];
|
|
23577
|
-
const here =
|
|
23924
|
+
const here = dirname19(fileURLToPath6(import.meta.url));
|
|
23578
23925
|
const candidates = [
|
|
23579
23926
|
// dev: src/cli/commands/ → repo-root .claude/skills/<slug>/SKILL.md
|
|
23580
23927
|
resolve36(here, "../../..", spec.sourcePath),
|
|
@@ -23584,8 +23931,8 @@ function readTutorialFromDisk(variant) {
|
|
|
23584
23931
|
resolve36(here, "../cli/tutorial", spec.bundledName)
|
|
23585
23932
|
];
|
|
23586
23933
|
for (const candidate of candidates) {
|
|
23587
|
-
if (
|
|
23588
|
-
return
|
|
23934
|
+
if (existsSync29(candidate)) {
|
|
23935
|
+
return readFileSync19(candidate, "utf8");
|
|
23589
23936
|
}
|
|
23590
23937
|
}
|
|
23591
23938
|
throw new Error(`SKILL.md not found in any candidate location (last tried: ${candidates[candidates.length - 1]})`);
|
|
@@ -23758,7 +24105,7 @@ await lifecycleDispatcher.dispatch(
|
|
|
23758
24105
|
process.exit(exitCode);
|
|
23759
24106
|
function resolveBareDefault() {
|
|
23760
24107
|
const ctx = defaultRuntimeContext();
|
|
23761
|
-
if (
|
|
24108
|
+
if (existsSync30(defaultProjectDbPath(ctx))) {
|
|
23762
24109
|
return ["serve"];
|
|
23763
24110
|
}
|
|
23764
24111
|
process.stderr.write(tx(ENTRY_TEXTS.bareNoProject, { cwd: ctx.cwd }));
|