summer-engine 2.3.0 → 2.5.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +20 -5
- package/.codex-plugin/plugin.json +15 -5
- package/.cursor-plugin/plugin.json +15 -5
- package/.factory-plugin/plugin.json +2 -2
- package/.opencode/INSTALL.md +1 -1
- package/CHANGELOG.md +27 -6
- package/CLAUDE.md +2 -2
- package/GEMINI.md +2 -3
- package/README.md +51 -30
- package/_persona/summer/SKILL.md +5 -6
- package/commands/gameskill.md +103 -0
- package/dist/bin/summer.js +12 -4
- package/dist/commands/create.js +2 -0
- package/dist/commands/doctor.js +71 -1
- package/dist/commands/install.d.ts +17 -0
- package/dist/commands/install.js +63 -4
- package/dist/commands/memory.d.ts +14 -0
- package/dist/commands/memory.js +196 -0
- package/dist/commands/setup.js +8 -2
- package/dist/commands/skills.js +90 -6
- package/dist/commands/status.js +3 -0
- package/dist/lib/agent-config.d.ts +2 -2
- package/dist/lib/agent-config.js +134 -7
- package/dist/lib/api-client.d.ts +13 -0
- package/dist/lib/api-client.js +63 -26
- package/dist/lib/async-op-lifecycle.d.ts +60 -0
- package/dist/lib/async-op-lifecycle.js +83 -0
- package/dist/lib/project-memory.d.ts +33 -0
- package/dist/lib/project-memory.js +220 -0
- package/dist/lib/skills-registry.d.ts +134 -61
- package/dist/lib/skills-registry.js +102 -4
- package/dist/lib/version-check.d.ts +106 -0
- package/dist/lib/version-check.js +338 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +81 -0
- package/dist/mcp/tools/debug-tools.js +32 -2
- package/dist/mcp/tools/project-tools.js +18 -6
- package/dist/mcp/tools/with-engine.d.ts +14 -0
- package/dist/mcp/tools/with-engine.js +52 -7
- package/gemini-extension.json +2 -2
- package/hooks/pre-commit-doctor.sh +0 -0
- package/hooks/run-hook.cmd +0 -0
- package/hooks/session-start.sh +0 -0
- package/package.json +6 -4
- package/skills/2d-assets/character-portrait/SKILL.md +1 -1
- package/skills/2d-assets/concept-art/SKILL.md +1 -1
- package/skills/2d-assets/create-asset-sheet/SKILL.md +163 -0
- package/skills/2d-assets/instantiate-asset-pack/SKILL.md +245 -0
- package/skills/2d-assets/pixel-art/SKILL.md +1 -1
- package/skills/2d-assets/skybox-panorama/SKILL.md +1 -1
- package/skills/2d-assets/sprite-sheet/SKILL.md +1 -1
- package/skills/2d-assets/tileable-texture/SKILL.md +1 -1
- package/skills/2d-assets/ui-graphics/SKILL.md +1 -1
- package/skills/2d-assets/use-widget-asset/SKILL.md +217 -0
- package/skills/3d-assets/character-model/SKILL.md +1 -1
- package/skills/3d-assets/organic-model/SKILL.md +1 -1
- package/skills/3d-assets/prop-model/SKILL.md +1 -1
- package/skills/3d-assets/vehicle-model/SKILL.md +1 -1
- package/skills/README.md +37 -30
- package/skills/ai-and-npcs/design-npc/SKILL.md +6 -6
- package/skills/animation/animation-tree/SKILL.md +2 -2
- package/skills/animation/facial-and-lipsync/SKILL.md +1 -1
- package/skills/animation/generate-motion/SKILL.md +1 -1
- package/skills/animation/procedural-animation/SKILL.md +2 -2
- package/skills/audio/adaptive-music/SKILL.md +1 -1
- package/skills/audio/audio-direction/SKILL.md +5 -5
- package/skills/audio/sound-effect/SKILL.md +2 -2
- package/skills/audio/voice-line/SKILL.md +25 -3
- package/skills/character-controllers/fps-controller/SKILL.md +5 -5
- package/skills/debugging/debug/SKILL.md +39 -1
- package/skills/deployment/export-and-ship/SKILL.md +4 -4
- package/skills/gameplay-mechanics/auto-fire-targeting/SKILL.md +170 -0
- package/skills/gameplay-mechanics/design-mechanic/SKILL.md +8 -8
- package/skills/level-design/design-level/SKILL.md +5 -5
- package/skills/level-design/scene-to-level/SKILL.md +125 -0
- package/skills/multiplayer-and-networking/host-authoritative-state/SKILL.md +4 -4
- package/skills/multiplayer-and-networking/peer-to-peer-multiplayer/SKILL.md +4 -4
- package/skills/multiplayer-and-networking/setup-multiplayer/SKILL.md +7 -7
- package/skills/performance/tune-performance/SKILL.md +5 -5
- package/skills/rendering-and-lighting/3d-lighting/SKILL.md +1 -1
- package/skills/rendering-and-lighting/art-direction/SKILL.md +5 -5
- package/skills/scene-and-project/brainstorm-game/SKILL.md +4 -4
- package/skills/scene-and-project/play/SKILL.md +1 -1
- package/skills/scene-and-project/scene-composition/SKILL.md +30 -1
- package/skills/scripting-patterns/gdscript-patterns/SKILL.md +53 -0
- package/skills/ui-and-ux/ui-basics/SKILL.md +1 -1
- package/skills/video/animated-loop/SKILL.md +1 -1
- package/skills/video/cinematic-cutscene/SKILL.md +1 -1
- package/skills/video/trailer-shot/SKILL.md +1 -1
- package/skills/visual-effects/game-feel/SKILL.md +6 -6
- package/skills/workflow/brainstorming/SKILL.md +164 -0
- package/skills/workflow/brainstorming/scripts/frame-template.html +214 -0
- package/skills/workflow/brainstorming/scripts/helper.js +88 -0
- package/skills/workflow/brainstorming/scripts/server.cjs +354 -0
- package/skills/workflow/brainstorming/scripts/start-server.sh +148 -0
- package/skills/workflow/brainstorming/scripts/stop-server.sh +56 -0
- package/skills/workflow/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/skills/workflow/brainstorming/visual-companion.md +287 -0
- package/skills/workflow/debugging-game-feel/SKILL.md +174 -0
- package/skills/workflow/diagnosing-perf-regressions/SKILL.md +190 -0
- package/skills/workflow/dispatching-parallel-agents/SKILL.md +182 -0
- package/skills/workflow/gameskill/SKILL.md +111 -0
- package/skills/workflow/investigating-bugs/CREATION-LOG.md +119 -0
- package/skills/workflow/investigating-bugs/SKILL.md +296 -0
- package/skills/workflow/investigating-bugs/condition-based-waiting-example.ts +158 -0
- package/skills/workflow/investigating-bugs/condition-based-waiting.md +115 -0
- package/skills/workflow/investigating-bugs/defense-in-depth.md +122 -0
- package/skills/workflow/investigating-bugs/find-polluter.sh +63 -0
- package/skills/workflow/investigating-bugs/root-cause-tracing.md +169 -0
- package/skills/workflow/investigating-bugs/test-academic.md +14 -0
- package/skills/workflow/investigating-bugs/test-pressure-1.md +58 -0
- package/skills/workflow/investigating-bugs/test-pressure-2.md +68 -0
- package/skills/workflow/investigating-bugs/test-pressure-3.md +69 -0
- package/skills/workflow/playtesting-a-feature/SKILL.md +173 -0
- package/skills/{_meta → workflow}/skill-create/SKILL.md +32 -22
- package/skills/{_meta → workflow}/skill-improve/SKILL.md +9 -10
- package/skills/{_meta → workflow}/skill-test/SKILL.md +12 -12
- package/skills/{_meta → workflow}/using-summer/SKILL.md +5 -2
- package/skills/workflow/verification-before-completion/SKILL.md +139 -0
- package/skills/workflow/writing-plans/SKILL.md +152 -0
- package/skills/workflow/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/skills/workflow/writing-skills/SKILL.md +655 -0
- package/skills/workflow/writing-skills/anthropic-best-practices.md +1150 -0
- package/skills/workflow/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/workflow/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/workflow/writing-skills/persuasion-principles.md +187 -0
- package/skills/workflow/writing-skills/render-graphs.js +168 -0
- package/skills/workflow/writing-skills/testing-skills-with-subagents.md +384 -0
- package/dist/commands/assets.d.ts +0 -2
- package/dist/commands/assets.js +0 -189
- package/dist/commands/debug.d.ts +0 -2
- package/dist/commands/debug.js +0 -127
- package/dist/commands/doctor.test.d.ts +0 -1
- package/dist/commands/doctor.test.js +0 -29
- package/dist/commands/project.d.ts +0 -2
- package/dist/commands/project.js +0 -170
- package/dist/commands/scene.d.ts +0 -2
- package/dist/commands/scene.js +0 -290
- package/dist/lib/agent-config.test.d.ts +0 -1
- package/dist/lib/agent-config.test.js +0 -80
- package/dist/lib/format.test.d.ts +0 -1
- package/dist/lib/format.test.js +0 -36
- package/dist/lib/plugin-manifests.test.d.ts +0 -1
- package/dist/lib/plugin-manifests.test.js +0 -134
- package/dist/lib/with-engine.d.ts +0 -14
- package/dist/lib/with-engine.js +0 -109
- package/skills/_shared/collaborative-protocol.md +0 -50
- package/skills/_shared/gd-style.md +0 -121
- package/skills/_shared/godot-version.md +0 -32
- package/skills/_shared/mcp-tools-reference.md +0 -133
- package/skills/_shared/summer-folder.md +0 -44
- package/skills/_shared/template-registry.md +0 -40
- package/skills/_tests/runner.md +0 -90
- package/skills/_tests/specs/3d-lighting.md +0 -19
- package/skills/_tests/specs/art-direction.md +0 -115
- package/skills/_tests/specs/asset-strategy.md +0 -19
- package/skills/_tests/specs/audio-direction.md +0 -123
- package/skills/_tests/specs/brainstorm-game.md +0 -99
- package/skills/_tests/specs/debug.md +0 -94
- package/skills/_tests/specs/design-level.md +0 -113
- package/skills/_tests/specs/design-mechanic.md +0 -112
- package/skills/_tests/specs/design-npc.md +0 -143
- package/skills/_tests/specs/export-and-ship.md +0 -114
- package/skills/_tests/specs/fps-controller.md +0 -113
- package/skills/_tests/specs/gdscript-patterns.md +0 -19
- package/skills/_tests/specs/host-authoritative-state.md +0 -104
- package/skills/_tests/specs/make-game.md +0 -19
- package/skills/_tests/specs/peer-to-peer-multiplayer.md +0 -99
- package/skills/_tests/specs/play.md +0 -88
- package/skills/_tests/specs/scene-composition.md +0 -19
- package/skills/_tests/specs/setup-multiplayer.md +0 -106
- package/skills/_tests/specs/tune-performance.md +0 -104
- package/skills/_tests/specs/ui-basics.md +0 -19
- package/skills/_tests/specs/vfx.md +0 -116
- package/skills/catalog.yaml +0 -991
|
@@ -7,10 +7,12 @@ export const AGENT_CLIENTS = [
|
|
|
7
7
|
"cline",
|
|
8
8
|
"roo-code",
|
|
9
9
|
"gemini",
|
|
10
|
+
"github-copilot",
|
|
11
|
+
"vscode-copilot",
|
|
10
12
|
"opencode",
|
|
11
13
|
];
|
|
12
14
|
export const SKILL_CATEGORIES = [
|
|
13
|
-
"
|
|
15
|
+
"workflow",
|
|
14
16
|
"2d-assets",
|
|
15
17
|
"3d-assets",
|
|
16
18
|
"video",
|
|
@@ -37,9 +39,10 @@ export const SKILL_CATEGORIES = [
|
|
|
37
39
|
];
|
|
38
40
|
const ALL_CLIENTS = AGENT_CLIENTS;
|
|
39
41
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
42
|
+
* Install-time skill registry — every skill installable via `summer skills install`.
|
|
43
|
+
* Must stay in sync with `.claude-plugin/plugin.json` (and sibling manifests) for
|
|
44
|
+
* Claude Code / Cursor / Codex plugin discovery. The `plugin-manifests.test.ts`
|
|
45
|
+
* test fails the build if a skill exists on disk but not in the manifest.
|
|
43
46
|
*/
|
|
44
47
|
export const SKILL_REGISTRY = [
|
|
45
48
|
{
|
|
@@ -904,4 +907,99 @@ export const SKILL_REGISTRY = [
|
|
|
904
907
|
],
|
|
905
908
|
testScenario: "User wants 'a magical glow on a rune' — skill creates OmniLight3D with pulse-on-sin script + GPUParticles3D motes, uses additive billboard material from building blocks.",
|
|
906
909
|
},
|
|
910
|
+
{
|
|
911
|
+
name: "brainstorming",
|
|
912
|
+
category: "workflow",
|
|
913
|
+
public: true,
|
|
914
|
+
clients: ALL_CLIENTS,
|
|
915
|
+
recommended: true,
|
|
916
|
+
requiresMcpTools: [],
|
|
917
|
+
testScenario: "User says 'let's make a game' — skill explores intent, scope, and references through one-question-at-a-time dialogue before any code or scene work begins.",
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
name: "investigating-bugs",
|
|
921
|
+
category: "workflow",
|
|
922
|
+
public: true,
|
|
923
|
+
clients: ALL_CLIENTS,
|
|
924
|
+
recommended: true,
|
|
925
|
+
requiresMcpTools: [],
|
|
926
|
+
testScenario: "Encountering a bug whose first fix didn't stick — skill forces hypothesis-driven root cause analysis before another patch attempt.",
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
name: "verification-before-completion",
|
|
930
|
+
category: "workflow",
|
|
931
|
+
public: true,
|
|
932
|
+
clients: ALL_CLIENTS,
|
|
933
|
+
recommended: true,
|
|
934
|
+
requiresMcpTools: [],
|
|
935
|
+
testScenario: "About to claim a feature is done — skill blocks the claim until verification commands have been run and their output reviewed.",
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
name: "writing-plans",
|
|
939
|
+
category: "workflow",
|
|
940
|
+
public: true,
|
|
941
|
+
clients: ALL_CLIENTS,
|
|
942
|
+
recommended: false,
|
|
943
|
+
requiresMcpTools: [],
|
|
944
|
+
testScenario: "User has a multi-step spec — skill produces a written plan with checkpoints before any implementation begins.",
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
name: "dispatching-parallel-agents",
|
|
948
|
+
category: "workflow",
|
|
949
|
+
public: true,
|
|
950
|
+
clients: ALL_CLIENTS,
|
|
951
|
+
recommended: false,
|
|
952
|
+
requiresMcpTools: [],
|
|
953
|
+
testScenario: "Facing 2+ independent tasks — skill prepares isolated, self-contained agent prompts and dispatches them in parallel.",
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
name: "writing-skills",
|
|
957
|
+
category: "workflow",
|
|
958
|
+
public: true,
|
|
959
|
+
clients: ALL_CLIENTS,
|
|
960
|
+
recommended: false,
|
|
961
|
+
requiresMcpTools: [],
|
|
962
|
+
testScenario: "User wants a new Summer skill — skill walks through frontmatter, body structure, and the auto-trigger discipline required for the skill to fire reliably.",
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
name: "playtesting-a-feature",
|
|
966
|
+
category: "workflow",
|
|
967
|
+
public: true,
|
|
968
|
+
clients: ALL_CLIENTS,
|
|
969
|
+
recommended: true,
|
|
970
|
+
requiresMcpTools: [
|
|
971
|
+
"summer_play",
|
|
972
|
+
"summer_stop",
|
|
973
|
+
"summer_get_console",
|
|
974
|
+
"summer_get_debugger_errors",
|
|
975
|
+
"summer_get_diagnostics",
|
|
976
|
+
],
|
|
977
|
+
testScenario: "Just finished a gameplay feature — skill drives a golden-path + edge-case playthrough with diagnostics capture before the feature can be marked done.",
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
name: "debugging-game-feel",
|
|
981
|
+
category: "workflow",
|
|
982
|
+
public: true,
|
|
983
|
+
clients: ALL_CLIENTS,
|
|
984
|
+
recommended: false,
|
|
985
|
+
requiresMcpTools: [
|
|
986
|
+
"summer_play",
|
|
987
|
+
"summer_stop",
|
|
988
|
+
"summer_inspect_node",
|
|
989
|
+
],
|
|
990
|
+
testScenario: "User says 'the jump feels floaty' — skill isolates one variable at a time against a reference anchor, avoids compounding tweaks.",
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
name: "diagnosing-perf-regressions",
|
|
994
|
+
category: "workflow",
|
|
995
|
+
public: true,
|
|
996
|
+
clients: ALL_CLIENTS,
|
|
997
|
+
recommended: false,
|
|
998
|
+
requiresMcpTools: [
|
|
999
|
+
"summer_get_diagnostics",
|
|
1000
|
+
"summer_play",
|
|
1001
|
+
"summer_stop",
|
|
1002
|
+
],
|
|
1003
|
+
testScenario: "FPS dropped from 60 to 30 — skill bisects recent changes, captures before/after diagnostics, identifies the regression cause before tuning.",
|
|
1004
|
+
},
|
|
907
1005
|
];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version drift detection helpers used by `summer doctor` and the MCP server
|
|
3
|
+
* boot path.
|
|
4
|
+
*
|
|
5
|
+
* Two checks are exposed:
|
|
6
|
+
* - cli-version-current: compares installed package.json version against the
|
|
7
|
+
* latest published `summer-engine` on the npm registry.
|
|
8
|
+
* - skills-version-stale: reads a `.summer-version` marker written into the
|
|
9
|
+
* skill install dir (per-agent) and compares against the current CLI.
|
|
10
|
+
*
|
|
11
|
+
* Both are designed to be silent on offline / network errors — we never want
|
|
12
|
+
* to false-alarm a user just because their wifi is flaky.
|
|
13
|
+
*/
|
|
14
|
+
export interface SemverParts {
|
|
15
|
+
major: number;
|
|
16
|
+
minor: number;
|
|
17
|
+
patch: number;
|
|
18
|
+
}
|
|
19
|
+
export type DriftSeverity = "ok" | "warning" | "fail";
|
|
20
|
+
export interface DriftClassification {
|
|
21
|
+
severity: DriftSeverity;
|
|
22
|
+
reason: "current" | "ahead" | "patch-only" | "minor-drift" | "major-drift" | "behind";
|
|
23
|
+
}
|
|
24
|
+
export declare function parseSemver(version: string): SemverParts | null;
|
|
25
|
+
export declare function compareSemver(a: SemverParts, b: SemverParts): number;
|
|
26
|
+
/**
|
|
27
|
+
* Classify drift between an installed and a target version.
|
|
28
|
+
*
|
|
29
|
+
* ok installed >= target, OR installed is behind only on patch
|
|
30
|
+
* OR installed differs only in pre-release/build metadata.
|
|
31
|
+
* warning one minor version behind on the same major.
|
|
32
|
+
* fail two or more minors behind, or any major-version drift behind.
|
|
33
|
+
*/
|
|
34
|
+
export declare function classifyDrift(installed: SemverParts, target: SemverParts): DriftClassification;
|
|
35
|
+
export interface RegistryFetchOptions {
|
|
36
|
+
/** Override the default fetch (used in tests). Must implement the global fetch shape. */
|
|
37
|
+
fetchImpl?: typeof fetch;
|
|
38
|
+
/** Override the timeout. */
|
|
39
|
+
timeoutMs?: number;
|
|
40
|
+
/** Override the URL (used in tests). */
|
|
41
|
+
url?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface RegistryFetchResult {
|
|
44
|
+
ok: true;
|
|
45
|
+
version: string;
|
|
46
|
+
}
|
|
47
|
+
export interface RegistryFetchFailure {
|
|
48
|
+
ok: false;
|
|
49
|
+
reason: "registry-unreachable";
|
|
50
|
+
message?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function fetchLatestRegistryVersion(options?: RegistryFetchOptions): Promise<RegistryFetchResult | RegistryFetchFailure>;
|
|
53
|
+
/**
|
|
54
|
+
* Build the `cli-version-current` doctor check. Pure helper so tests can drive
|
|
55
|
+
* registry responses through `fetchImpl` rather than touching the network.
|
|
56
|
+
*/
|
|
57
|
+
export interface CliVersionCheckInput {
|
|
58
|
+
installedVersion: string;
|
|
59
|
+
registry: RegistryFetchResult | RegistryFetchFailure;
|
|
60
|
+
}
|
|
61
|
+
export interface CliVersionCheckOutput {
|
|
62
|
+
status: DriftSeverity;
|
|
63
|
+
message: string;
|
|
64
|
+
details: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
export declare function buildCliVersionCheck(input: CliVersionCheckInput): CliVersionCheckOutput;
|
|
67
|
+
export declare const SKILL_VERSION_MARKER_FILENAME = ".summer-version";
|
|
68
|
+
export interface SkillVersionMarker {
|
|
69
|
+
version: string;
|
|
70
|
+
installedAt: string;
|
|
71
|
+
}
|
|
72
|
+
export declare function readSkillMarker(dir: string): SkillVersionMarker | null;
|
|
73
|
+
export declare function writeSkillMarker(dir: string, version: string, now?: Date): string;
|
|
74
|
+
/**
|
|
75
|
+
* Candidate locations for skill-version markers. Listed roughly in order of
|
|
76
|
+
* how likely a Summer user is to have them populated. We probe every candidate,
|
|
77
|
+
* not just the first one — if multiple agents have markers, the most-stale one
|
|
78
|
+
* wins so we surface the worst case to the user.
|
|
79
|
+
*/
|
|
80
|
+
export interface SkillMarkerCandidate {
|
|
81
|
+
agent: string;
|
|
82
|
+
dir: string;
|
|
83
|
+
}
|
|
84
|
+
export declare function defaultSkillMarkerCandidates(): SkillMarkerCandidate[];
|
|
85
|
+
export interface SkillsVersionCheckInput {
|
|
86
|
+
installedCliVersion: string;
|
|
87
|
+
candidates: SkillMarkerCandidate[];
|
|
88
|
+
}
|
|
89
|
+
export interface SkillsVersionCheckOutput {
|
|
90
|
+
status: DriftSeverity;
|
|
91
|
+
message: string;
|
|
92
|
+
details: Record<string, unknown>;
|
|
93
|
+
}
|
|
94
|
+
export declare function buildSkillsVersionCheck(input: SkillsVersionCheckInput): SkillsVersionCheckOutput;
|
|
95
|
+
/**
|
|
96
|
+
* Helper used by the MCP server boot path to render a one-line drift notice.
|
|
97
|
+
* Returns null when no notice is needed (fresh, ahead, patch-only, offline).
|
|
98
|
+
*/
|
|
99
|
+
export interface BootDriftNotice {
|
|
100
|
+
installedVersion: string;
|
|
101
|
+
latestVersion: string;
|
|
102
|
+
agent?: string;
|
|
103
|
+
text: string;
|
|
104
|
+
}
|
|
105
|
+
export declare function buildBootDriftNotice(installedVersion: string, registry: RegistryFetchResult | RegistryFetchFailure, agent?: string): BootDriftNotice | null;
|
|
106
|
+
export declare function markerMtime(dir: string): number | null;
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version drift detection helpers used by `summer doctor` and the MCP server
|
|
3
|
+
* boot path.
|
|
4
|
+
*
|
|
5
|
+
* Two checks are exposed:
|
|
6
|
+
* - cli-version-current: compares installed package.json version against the
|
|
7
|
+
* latest published `summer-engine` on the npm registry.
|
|
8
|
+
* - skills-version-stale: reads a `.summer-version` marker written into the
|
|
9
|
+
* skill install dir (per-agent) and compares against the current CLI.
|
|
10
|
+
*
|
|
11
|
+
* Both are designed to be silent on offline / network errors — we never want
|
|
12
|
+
* to false-alarm a user just because their wifi is flaky.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync, readFileSync, writeFileSync, statSync } from "node:fs";
|
|
15
|
+
import { homedir, platform } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
const NPM_REGISTRY_LATEST_URL = "https://registry.npmjs.org/summer-engine/latest";
|
|
18
|
+
const REGISTRY_FETCH_TIMEOUT_MS = 3000;
|
|
19
|
+
export function parseSemver(version) {
|
|
20
|
+
if (!version)
|
|
21
|
+
return null;
|
|
22
|
+
// Strip any leading "v" and trim build / pre-release tags after the first hyphen.
|
|
23
|
+
const cleaned = version.trim().replace(/^v/, "").split(/[-+]/)[0];
|
|
24
|
+
const match = cleaned.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
25
|
+
if (!match)
|
|
26
|
+
return null;
|
|
27
|
+
const [, maj, min, pat] = match;
|
|
28
|
+
return {
|
|
29
|
+
major: Number(maj),
|
|
30
|
+
minor: Number(min),
|
|
31
|
+
patch: Number(pat),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function compareSemver(a, b) {
|
|
35
|
+
if (a.major !== b.major)
|
|
36
|
+
return a.major - b.major;
|
|
37
|
+
if (a.minor !== b.minor)
|
|
38
|
+
return a.minor - b.minor;
|
|
39
|
+
return a.patch - b.patch;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Classify drift between an installed and a target version.
|
|
43
|
+
*
|
|
44
|
+
* ok installed >= target, OR installed is behind only on patch
|
|
45
|
+
* OR installed differs only in pre-release/build metadata.
|
|
46
|
+
* warning one minor version behind on the same major.
|
|
47
|
+
* fail two or more minors behind, or any major-version drift behind.
|
|
48
|
+
*/
|
|
49
|
+
export function classifyDrift(installed, target) {
|
|
50
|
+
const cmp = compareSemver(installed, target);
|
|
51
|
+
if (cmp === 0)
|
|
52
|
+
return { severity: "ok", reason: "current" };
|
|
53
|
+
if (cmp > 0)
|
|
54
|
+
return { severity: "ok", reason: "ahead" };
|
|
55
|
+
// installed < target
|
|
56
|
+
if (installed.major < target.major) {
|
|
57
|
+
return { severity: "fail", reason: "major-drift" };
|
|
58
|
+
}
|
|
59
|
+
if (installed.minor === target.minor) {
|
|
60
|
+
// Same major + minor, target patch is higher → patch-only drift.
|
|
61
|
+
return { severity: "ok", reason: "patch-only" };
|
|
62
|
+
}
|
|
63
|
+
const minorDelta = target.minor - installed.minor;
|
|
64
|
+
if (minorDelta >= 2) {
|
|
65
|
+
return { severity: "fail", reason: "minor-drift" };
|
|
66
|
+
}
|
|
67
|
+
return { severity: "warning", reason: "minor-drift" };
|
|
68
|
+
}
|
|
69
|
+
export async function fetchLatestRegistryVersion(options = {}) {
|
|
70
|
+
const url = options.url ?? NPM_REGISTRY_LATEST_URL;
|
|
71
|
+
const timeoutMs = options.timeoutMs ?? REGISTRY_FETCH_TIMEOUT_MS;
|
|
72
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
73
|
+
if (!fetchImpl) {
|
|
74
|
+
return { ok: false, reason: "registry-unreachable", message: "fetch unavailable" };
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const response = await fetchImpl(url, {
|
|
78
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
79
|
+
headers: { accept: "application/json" },
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
return {
|
|
83
|
+
ok: false,
|
|
84
|
+
reason: "registry-unreachable",
|
|
85
|
+
message: `registry responded ${response.status}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const body = (await response.json());
|
|
89
|
+
if (typeof body.version !== "string") {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
reason: "registry-unreachable",
|
|
93
|
+
message: "registry payload missing version",
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { ok: true, version: body.version };
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
reason: "registry-unreachable",
|
|
102
|
+
message: error instanceof Error ? error.message : String(error),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const RECOMMENDED_REFRESH_COMMAND = "npx clear-npx-cache && npx -y summer-engine@latest setup <agent> --yes --force";
|
|
107
|
+
export function buildCliVersionCheck(input) {
|
|
108
|
+
if (!input.registry.ok) {
|
|
109
|
+
return {
|
|
110
|
+
status: "ok",
|
|
111
|
+
message: "registry unreachable, skipping check",
|
|
112
|
+
details: {
|
|
113
|
+
installedVersion: input.installedVersion,
|
|
114
|
+
reason: input.registry.reason,
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const installed = parseSemver(input.installedVersion);
|
|
119
|
+
const latest = parseSemver(input.registry.version);
|
|
120
|
+
if (!installed || !latest) {
|
|
121
|
+
// If we can't parse either side, don't false-alarm.
|
|
122
|
+
return {
|
|
123
|
+
status: "ok",
|
|
124
|
+
message: "could not compare versions",
|
|
125
|
+
details: {
|
|
126
|
+
installedVersion: input.installedVersion,
|
|
127
|
+
latestVersion: input.registry.version,
|
|
128
|
+
reason: "unparseable-version",
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const drift = classifyDrift(installed, latest);
|
|
133
|
+
const baseDetails = {
|
|
134
|
+
installedVersion: input.installedVersion,
|
|
135
|
+
latestVersion: input.registry.version,
|
|
136
|
+
};
|
|
137
|
+
switch (drift.severity) {
|
|
138
|
+
case "ok":
|
|
139
|
+
return {
|
|
140
|
+
status: "ok",
|
|
141
|
+
message: drift.reason === "patch-only"
|
|
142
|
+
? `v${input.installedVersion} (latest ${input.registry.version}, patch-only)`
|
|
143
|
+
: drift.reason === "ahead"
|
|
144
|
+
? `v${input.installedVersion} (ahead of latest ${input.registry.version})`
|
|
145
|
+
: `v${input.installedVersion}`,
|
|
146
|
+
details: { ...baseDetails, reason: drift.reason },
|
|
147
|
+
};
|
|
148
|
+
case "warning":
|
|
149
|
+
return {
|
|
150
|
+
status: "warning",
|
|
151
|
+
message: `v${input.installedVersion} → v${input.registry.version} available`,
|
|
152
|
+
details: {
|
|
153
|
+
...baseDetails,
|
|
154
|
+
recommendedAction: RECOMMENDED_REFRESH_COMMAND,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
case "fail":
|
|
158
|
+
return {
|
|
159
|
+
status: "fail",
|
|
160
|
+
message: `v${input.installedVersion} is significantly behind v${input.registry.version}`,
|
|
161
|
+
details: {
|
|
162
|
+
...baseDetails,
|
|
163
|
+
recommendedAction: RECOMMENDED_REFRESH_COMMAND,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Skill marker (.summer-version) helpers
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
export const SKILL_VERSION_MARKER_FILENAME = ".summer-version";
|
|
172
|
+
export function readSkillMarker(dir) {
|
|
173
|
+
const path = join(dir, SKILL_VERSION_MARKER_FILENAME);
|
|
174
|
+
if (!existsSync(path))
|
|
175
|
+
return null;
|
|
176
|
+
try {
|
|
177
|
+
const raw = readFileSync(path, "utf-8");
|
|
178
|
+
const parsed = JSON.parse(raw);
|
|
179
|
+
if (typeof parsed.version !== "string" || typeof parsed.installedAt !== "string") {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return { version: parsed.version, installedAt: parsed.installedAt };
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export function writeSkillMarker(dir, version, now = new Date()) {
|
|
189
|
+
const path = join(dir, SKILL_VERSION_MARKER_FILENAME);
|
|
190
|
+
const payload = {
|
|
191
|
+
version,
|
|
192
|
+
installedAt: now.toISOString(),
|
|
193
|
+
};
|
|
194
|
+
writeFileSync(path, JSON.stringify(payload, null, 2) + "\n", "utf-8");
|
|
195
|
+
return path;
|
|
196
|
+
}
|
|
197
|
+
export function defaultSkillMarkerCandidates() {
|
|
198
|
+
const home = homedir();
|
|
199
|
+
const candidates = [
|
|
200
|
+
{ agent: "claude-code", dir: join(home, ".claude", "skills") },
|
|
201
|
+
{ agent: "codex", dir: join(home, ".agents", "skills") },
|
|
202
|
+
{ agent: "summer", dir: join(home, ".summer", "skills") },
|
|
203
|
+
{ agent: "cline", dir: join(home, "Documents", "Cline", "Rules") },
|
|
204
|
+
{ agent: "roo-code", dir: join(home, "Documents", "Roo", "Rules") },
|
|
205
|
+
{
|
|
206
|
+
agent: "gemini",
|
|
207
|
+
dir: join(home, ".gemini", "extensions", "summer-engine", "skills"),
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
// OpenCode user-scope agent dir is OS-dependent.
|
|
211
|
+
if (platform() === "win32") {
|
|
212
|
+
const appData = process.env.APPDATA ?? join(home, "AppData", "Roaming");
|
|
213
|
+
candidates.push({
|
|
214
|
+
agent: "opencode",
|
|
215
|
+
dir: join(appData, "opencode", "agents", "summer"),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const xdg = process.env.XDG_CONFIG_HOME ?? join(home, ".config");
|
|
220
|
+
candidates.push({
|
|
221
|
+
agent: "opencode",
|
|
222
|
+
dir: join(xdg, "opencode", "agents", "summer"),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return candidates;
|
|
226
|
+
}
|
|
227
|
+
export function buildSkillsVersionCheck(input) {
|
|
228
|
+
const cliParsed = parseSemver(input.installedCliVersion);
|
|
229
|
+
if (!cliParsed) {
|
|
230
|
+
return {
|
|
231
|
+
status: "ok",
|
|
232
|
+
message: "could not parse CLI version",
|
|
233
|
+
details: { reason: "unparseable-cli-version" },
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const resolutions = [];
|
|
237
|
+
for (const candidate of input.candidates) {
|
|
238
|
+
const marker = readSkillMarker(candidate.dir);
|
|
239
|
+
if (!marker)
|
|
240
|
+
continue;
|
|
241
|
+
const markerSemver = parseSemver(marker.version);
|
|
242
|
+
if (!markerSemver)
|
|
243
|
+
continue;
|
|
244
|
+
const drift = classifyDrift(markerSemver, cliParsed);
|
|
245
|
+
const installedAtMs = Date.parse(marker.installedAt);
|
|
246
|
+
resolutions.push({
|
|
247
|
+
agent: candidate.agent,
|
|
248
|
+
dir: candidate.dir,
|
|
249
|
+
marker,
|
|
250
|
+
drift,
|
|
251
|
+
installedAtMs: Number.isFinite(installedAtMs) ? installedAtMs : 0,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
if (resolutions.length === 0) {
|
|
255
|
+
return {
|
|
256
|
+
status: "ok",
|
|
257
|
+
message: "no skill marker yet (treated as fresh install)",
|
|
258
|
+
details: { reason: "no-marker" },
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
// Pick the worst (most-stale) marker. Severity rank fail > warning > ok;
|
|
262
|
+
// tiebreak by oldest installedAt.
|
|
263
|
+
const severityRank = { fail: 2, warning: 1, ok: 0 };
|
|
264
|
+
const worst = [...resolutions].sort((a, b) => {
|
|
265
|
+
const sevDiff = severityRank[b.drift.severity] - severityRank[a.drift.severity];
|
|
266
|
+
if (sevDiff !== 0)
|
|
267
|
+
return sevDiff;
|
|
268
|
+
return a.installedAtMs - b.installedAtMs;
|
|
269
|
+
})[0];
|
|
270
|
+
const baseDetails = {
|
|
271
|
+
agent: worst.agent,
|
|
272
|
+
skillsDir: worst.dir,
|
|
273
|
+
markerVersion: worst.marker.version,
|
|
274
|
+
installedAt: worst.marker.installedAt,
|
|
275
|
+
cliVersion: input.installedCliVersion,
|
|
276
|
+
};
|
|
277
|
+
switch (worst.drift.severity) {
|
|
278
|
+
case "ok":
|
|
279
|
+
return {
|
|
280
|
+
status: "ok",
|
|
281
|
+
message: `skills v${worst.marker.version} match CLI`,
|
|
282
|
+
details: baseDetails,
|
|
283
|
+
};
|
|
284
|
+
case "warning":
|
|
285
|
+
return {
|
|
286
|
+
status: "warning",
|
|
287
|
+
message: `skills v${worst.marker.version} are one minor version behind v${input.installedCliVersion}`,
|
|
288
|
+
details: {
|
|
289
|
+
...baseDetails,
|
|
290
|
+
recommendedAction: skillsRefreshCommand(worst.agent),
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
case "fail":
|
|
294
|
+
return {
|
|
295
|
+
status: "fail",
|
|
296
|
+
message: `skills v${worst.marker.version} are significantly behind v${input.installedCliVersion}`,
|
|
297
|
+
details: {
|
|
298
|
+
...baseDetails,
|
|
299
|
+
recommendedAction: skillsRefreshCommand(worst.agent),
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function skillsRefreshCommand(agent) {
|
|
305
|
+
return `npx clear-npx-cache && npx -y summer-engine@latest setup ${agent} --yes --force`;
|
|
306
|
+
}
|
|
307
|
+
export function buildBootDriftNotice(installedVersion, registry, agent) {
|
|
308
|
+
if (!registry.ok)
|
|
309
|
+
return null;
|
|
310
|
+
const installed = parseSemver(installedVersion);
|
|
311
|
+
const latest = parseSemver(registry.version);
|
|
312
|
+
if (!installed || !latest)
|
|
313
|
+
return null;
|
|
314
|
+
const drift = classifyDrift(installed, latest);
|
|
315
|
+
if (drift.severity === "ok")
|
|
316
|
+
return null;
|
|
317
|
+
const agentSlug = agent ?? "<agent>";
|
|
318
|
+
const text = `[summer] CLI ${installedVersion} — latest ${registry.version}; ` +
|
|
319
|
+
`run \`npx clear-npx-cache && npx -y summer-engine@latest setup ${agentSlug} --yes --force\` to update.`;
|
|
320
|
+
return {
|
|
321
|
+
installedVersion,
|
|
322
|
+
latestVersion: registry.version,
|
|
323
|
+
agent,
|
|
324
|
+
text,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
// Re-export for tests / callers that want to know the install marker's mtime.
|
|
328
|
+
export function markerMtime(dir) {
|
|
329
|
+
const path = join(dir, SKILL_VERSION_MARKER_FILENAME);
|
|
330
|
+
if (!existsSync(path))
|
|
331
|
+
return null;
|
|
332
|
+
try {
|
|
333
|
+
return statSync(path).mtimeMs;
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
package/dist/mcp/server.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { EngineApiClient } from "../lib/api-client.js";
|
|
2
|
+
import { type BootDriftNotice } from "../lib/version-check.js";
|
|
3
|
+
export declare function getCachedBootDriftNotice(): BootDriftNotice | null;
|
|
2
4
|
export declare function getClient(): Promise<EngineApiClient>;
|
|
3
5
|
export declare function resetClient(): void;
|
|
4
6
|
export declare function startMcpServer(): Promise<void>;
|
package/dist/mcp/server.js
CHANGED
|
@@ -7,8 +7,25 @@ import { registerDebugTools } from "./tools/debug-tools.js";
|
|
|
7
7
|
import { registerProjectTools } from "./tools/project-tools.js";
|
|
8
8
|
import { registerAssetTools } from "./tools/asset-tools.js";
|
|
9
9
|
import { registerGenerateTools } from "./tools/generate-tools.js";
|
|
10
|
+
import { buildBootDriftNotice, fetchLatestRegistryVersion, } from "../lib/version-check.js";
|
|
10
11
|
const require = createRequire(import.meta.url);
|
|
11
12
|
const { version } = require("../../package.json");
|
|
13
|
+
let cachedBootDriftNotice = null;
|
|
14
|
+
export function getCachedBootDriftNotice() {
|
|
15
|
+
return cachedBootDriftNotice;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Fire-and-forget probe of the npm registry on MCP boot. Caches the result for
|
|
19
|
+
* the session. We never await this in startup so a slow / offline registry
|
|
20
|
+
* never blocks tool registration. Tools that surface the notice should call
|
|
21
|
+
* `getCachedBootDriftNotice()` and skip silently when null.
|
|
22
|
+
*/
|
|
23
|
+
async function probeBootDrift() {
|
|
24
|
+
const registry = await fetchLatestRegistryVersion();
|
|
25
|
+
// Agent is unknown at the MCP layer; use a generic placeholder. Doctor still
|
|
26
|
+
// prescribes the correct agent-aware command via setup recommendations.
|
|
27
|
+
cachedBootDriftNotice = buildBootDriftNotice(version, registry);
|
|
28
|
+
}
|
|
12
29
|
let cachedClient = null;
|
|
13
30
|
export async function getClient() {
|
|
14
31
|
if (cachedClient) {
|
|
@@ -26,16 +43,80 @@ export async function getClient() {
|
|
|
26
43
|
export function resetClient() {
|
|
27
44
|
cachedClient = null;
|
|
28
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Passive result-size observability. Monkey-patches server.tool() so every
|
|
48
|
+
* registered handler's serialized text payload is measured and logged to
|
|
49
|
+
* stderr in the existing `[summer-mcp]` JSON shape. The result itself is
|
|
50
|
+
* never modified — external coding agents (Claude Code, Cursor, Codex)
|
|
51
|
+
* manage their own context budgets, and silently truncating their tool
|
|
52
|
+
* results is the wrong shape (see #102 revert). If a specific tool turns
|
|
53
|
+
* out to produce pathological payloads in practice, the right fix is
|
|
54
|
+
* adding range/depth/filter parameters to that tool — not a blanket cap.
|
|
55
|
+
*/
|
|
56
|
+
function installResultSizeLogger(server) {
|
|
57
|
+
const original = server.tool.bind(server);
|
|
58
|
+
server.tool = function patchedTool(...args) {
|
|
59
|
+
if (args.length < 2)
|
|
60
|
+
return original(...args);
|
|
61
|
+
const name = args[0];
|
|
62
|
+
if (typeof name !== "string")
|
|
63
|
+
return original(...args);
|
|
64
|
+
const lastIdx = args.length - 1;
|
|
65
|
+
const handler = args[lastIdx];
|
|
66
|
+
if (typeof handler !== "function")
|
|
67
|
+
return original(...args);
|
|
68
|
+
const wrapped = async (...handlerArgs) => {
|
|
69
|
+
const result = await handler(...handlerArgs);
|
|
70
|
+
try {
|
|
71
|
+
if (result &&
|
|
72
|
+
typeof result === "object" &&
|
|
73
|
+
"content" in result &&
|
|
74
|
+
Array.isArray(result.content)) {
|
|
75
|
+
let chars = 0;
|
|
76
|
+
for (const entry of result
|
|
77
|
+
.content) {
|
|
78
|
+
if (entry && entry.type === "text" && typeof entry.text === "string") {
|
|
79
|
+
chars += entry.text.length;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const bytes = Buffer.byteLength(result.content
|
|
83
|
+
.filter((e) => e && e.type === "text" && typeof e.text === "string")
|
|
84
|
+
.map((e) => e.text)
|
|
85
|
+
.join(""), "utf8");
|
|
86
|
+
const line = JSON.stringify({
|
|
87
|
+
event: "mcp:result_size",
|
|
88
|
+
tool_name: name,
|
|
89
|
+
chars,
|
|
90
|
+
bytes,
|
|
91
|
+
});
|
|
92
|
+
process.stderr.write(`[summer-mcp] ${line}\n`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Telemetry must never break the tool call.
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
};
|
|
100
|
+
const newArgs = [...args];
|
|
101
|
+
newArgs[lastIdx] = wrapped;
|
|
102
|
+
return original(...newArgs);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
29
105
|
export async function startMcpServer() {
|
|
30
106
|
const server = new McpServer({
|
|
31
107
|
name: "summer-engine",
|
|
32
108
|
version,
|
|
33
109
|
});
|
|
110
|
+
// Passive observability: log result-size to stderr but do not modify
|
|
111
|
+
// results. See installResultSizeLogger above.
|
|
112
|
+
installResultSizeLogger(server);
|
|
34
113
|
registerSceneTools(server);
|
|
35
114
|
registerDebugTools(server);
|
|
36
115
|
registerProjectTools(server);
|
|
37
116
|
registerAssetTools(server);
|
|
38
117
|
registerGenerateTools(server);
|
|
118
|
+
// Fire-and-forget — never block tool registration on the npm registry.
|
|
119
|
+
void probeBootDrift();
|
|
39
120
|
const transport = new StdioServerTransport();
|
|
40
121
|
await server.connect(transport);
|
|
41
122
|
process.stderr.write(`[summer-mcp] MCP server running v${version}.\n`);
|