@sentry/junior 0.203.0 → 0.205.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.
Files changed (174) hide show
  1. package/dist/agent-D4nu8MQV.js +17521 -0
  2. package/dist/agent-hooks-DXHpTJ4m.js +4042 -0
  3. package/dist/agent-runner-C04colea.js +678 -0
  4. package/dist/api/schema.d.ts +2736 -2639
  5. package/dist/api/schema.js +4 -169
  6. package/dist/api.d.ts +23 -17
  7. package/dist/api.js +3881 -5326
  8. package/dist/app-Cb776lL8.d.ts +247 -0
  9. package/dist/app.d.ts +4 -520
  10. package/dist/app.js +9524 -11875
  11. package/dist/auth-pause-state-C2P3guS1.js +168 -0
  12. package/dist/catalog-runtime-CXw3asJD.js +7 -0
  13. package/dist/checkpoint-CrViJ8Ps.js +2450 -0
  14. package/dist/cli/chat.d.ts +20 -0
  15. package/dist/cli/chat.js +229 -336
  16. package/dist/cli/check.d.ts +8 -0
  17. package/dist/cli/check.js +513 -806
  18. package/dist/cli/env.d.ts +13 -0
  19. package/dist/cli/env.js +63 -12
  20. package/dist/cli/init.d.ts +3 -0
  21. package/dist/cli/init.js +90 -159
  22. package/dist/cli/main.d.ts +6 -0
  23. package/dist/cli/main.js +43 -64
  24. package/dist/cli/plugins.d.ts +13 -0
  25. package/dist/cli/plugins.js +148 -227
  26. package/dist/cli/run.d.ts +16 -0
  27. package/dist/cli/run.js +52 -8
  28. package/dist/cli/snapshot-create.d.ts +4 -0
  29. package/dist/cli/snapshot-create.js +71 -125
  30. package/dist/cli/upgrade.d.ts +15 -0
  31. package/dist/cli/upgrade.js +232 -335
  32. package/dist/config-BOtJQ7Pc.js +941 -0
  33. package/dist/context-B45_WqVX.js +298 -0
  34. package/dist/conversation-privacy-BZRA6Pes.js +182 -0
  35. package/dist/conversation-work-C6y3T5Hx.js +51 -0
  36. package/dist/credential-sync-Bep34IB_.js +2661 -0
  37. package/dist/db-BeyXKJOI.d.ts +5 -0
  38. package/dist/destinations-BeBiJeEM.js +29 -0
  39. package/dist/execution-stats-B6lgWxCK.js +373 -0
  40. package/dist/executor-BlUhFL7_.js +721 -0
  41. package/dist/experimental-CIXf7Uye.js +46 -0
  42. package/dist/history-ClE4toxe.d.ts +32 -0
  43. package/dist/history-CqTuSAdI.js +441 -0
  44. package/dist/instrumentation.d.ts +3 -3
  45. package/dist/instrumentation.js +130 -176
  46. package/dist/job-queue-DLMqIK74.js +63 -0
  47. package/dist/logging-BMJzdDlP.js +1210 -0
  48. package/dist/model-profile-CZmVoGdX.js +49 -0
  49. package/dist/nitro.d.ts +31 -36
  50. package/dist/nitro.js +275 -456
  51. package/dist/oauth-callback-server-WwG5_Zgm.js +130 -0
  52. package/dist/oauth-relay-CdCnu80W.js +62 -0
  53. package/dist/package-discovery-7a9YqLhE.js +455 -0
  54. package/dist/pending-auth-CUbXq06G.js +185 -0
  55. package/dist/plugin-module-BKGOXca2.js +81 -0
  56. package/dist/plugins-BI9A1vKA.d.ts +95 -0
  57. package/dist/plugins-egKWjgue.js +99 -0
  58. package/dist/private-trace-result-BLvBjy_e.js +16 -0
  59. package/dist/profile-T0tXE4zu.js +861 -0
  60. package/dist/registry-BA0__8ai.js +1419 -0
  61. package/dist/reporting-schema-DosTN__Q.js +105 -0
  62. package/dist/resolve-bvD1ORdv.js +546 -0
  63. package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
  64. package/dist/runner-BXYxfIUL.js +392 -0
  65. package/dist/sandbox-egress-signals-ngEPjSEW.js +46 -0
  66. package/dist/schema-Rn8-XEaN.js +1140 -0
  67. package/dist/sentry-CD3SdJl-.js +26 -0
  68. package/dist/skills-DJOlYdBs.js +316 -0
  69. package/dist/spawn-h0R1bTEo.js +29 -0
  70. package/dist/stats-3YZn-Swi.js +2648 -0
  71. package/dist/store-C5qxF2ac.d.ts +208 -0
  72. package/dist/task-queue-CoXwd2eq.js +136 -0
  73. package/dist/task-runner-B2_mJZj0.js +639 -0
  74. package/dist/thread-state-CqZ1m-es.js +431 -0
  75. package/dist/turn-execution-fI6PZ0iI.js +47 -0
  76. package/dist/turn-lifecycle-CcnwrOcp.js +54 -0
  77. package/dist/usage-B5SfMb3H.js +296 -0
  78. package/dist/usage-schema-CKaDkH_j.js +20 -0
  79. package/dist/validation-DfWcvhuS.js +40 -0
  80. package/dist/vercel-queue-DIkVcfK4.js +167 -0
  81. package/dist/vercel.d.ts +6 -6
  82. package/dist/vercel.js +14 -6
  83. package/dist/version-CnXZ614E.d.ts +5 -0
  84. package/dist/version-oD-oPrbO.js +8 -0
  85. package/dist/version.d.ts +2 -4
  86. package/dist/version.js +2 -6
  87. package/dist/web-authorization-Crbzlmzo.js +76 -0
  88. package/dist/web-input-BOuq3oKK.js +494 -0
  89. package/dist/work-B91LQKHr.js +509 -0
  90. package/dist/worker-BcRRTe6k.js +625 -0
  91. package/package.json +9 -9
  92. package/dist/agent-hooks-GB2VPTNA.js +0 -51
  93. package/dist/catalog-runtime-UBLIWD3O.js +0 -14
  94. package/dist/chunk-2LYHWMR4.js +0 -46
  95. package/dist/chunk-352OWAZH.js +0 -320
  96. package/dist/chunk-3Y47VMHV.js +0 -6
  97. package/dist/chunk-3YJSKOWF.js +0 -164
  98. package/dist/chunk-4KSSKTQ5.js +0 -229
  99. package/dist/chunk-4UO6FK4G.js +0 -64
  100. package/dist/chunk-5AHX53NQ.js +0 -717
  101. package/dist/chunk-5J3E5FWA.js +0 -700
  102. package/dist/chunk-5PDAG6O3.js +0 -206
  103. package/dist/chunk-6Y5B5PEX.js +0 -80
  104. package/dist/chunk-7GWQ5S5R.js +0 -2086
  105. package/dist/chunk-7H2VZTWN.js +0 -103
  106. package/dist/chunk-7NL7T6VU.js +0 -406
  107. package/dist/chunk-A5L27LE4.js +0 -387
  108. package/dist/chunk-ADIAWL34.js +0 -81
  109. package/dist/chunk-ASTEMBKJ.js +0 -519
  110. package/dist/chunk-CEMMLV5L.js +0 -98
  111. package/dist/chunk-CM5HNV2W.js +0 -131
  112. package/dist/chunk-CRTRBBRI.js +0 -105
  113. package/dist/chunk-DA2KGZZI.js +0 -13
  114. package/dist/chunk-DIB4GX3G.js +0 -8
  115. package/dist/chunk-DKIJ2RP5.js +0 -68
  116. package/dist/chunk-DWPN6SCH.js +0 -172
  117. package/dist/chunk-EDKUFGPL.js +0 -64
  118. package/dist/chunk-ENY6SKIU.js +0 -1228
  119. package/dist/chunk-F4MGCZOX.js +0 -2943
  120. package/dist/chunk-FA6POGLH.js +0 -70
  121. package/dist/chunk-FSUR7GTG.js +0 -20475
  122. package/dist/chunk-GKEOHJL5.js +0 -1856
  123. package/dist/chunk-GMCB6W5Y.js +0 -333
  124. package/dist/chunk-IE2VPG7O.js +0 -1135
  125. package/dist/chunk-IZ5B5IHQ.js +0 -3656
  126. package/dist/chunk-JAL3CH4G.js +0 -27
  127. package/dist/chunk-KIIEUXD3.js +0 -1764
  128. package/dist/chunk-KXU4NIL7.js +0 -692
  129. package/dist/chunk-MTM5IFCY.js +0 -1190
  130. package/dist/chunk-N7TMSOZH.js +0 -235
  131. package/dist/chunk-NCEBK6PH.js +0 -36
  132. package/dist/chunk-NHJU2ZAL.js +0 -56
  133. package/dist/chunk-P6KTRNVC.js +0 -321
  134. package/dist/chunk-PDO5BLNM.js +0 -22
  135. package/dist/chunk-PDRNLLFT.js +0 -3295
  136. package/dist/chunk-PEQXYSSE.js +0 -45
  137. package/dist/chunk-PKEX2AQX.js +0 -198
  138. package/dist/chunk-PSJCGIGO.js +0 -1163
  139. package/dist/chunk-PZUSUL55.js +0 -506
  140. package/dist/chunk-R45M4TUS.js +0 -37
  141. package/dist/chunk-RC745GGZ.js +0 -122
  142. package/dist/chunk-SP3QKZXJ.js +0 -820
  143. package/dist/chunk-TXKUKMVO.js +0 -417
  144. package/dist/chunk-UE4GPWSD.js +0 -931
  145. package/dist/chunk-UIE3R5XU.js +0 -17
  146. package/dist/chunk-UXPG6ZIN.js +0 -84
  147. package/dist/chunk-VFUK3X5B.js +0 -50
  148. package/dist/chunk-VNMJWREK.js +0 -787
  149. package/dist/chunk-VZ7CSOX2.js +0 -706
  150. package/dist/chunk-WCNQJKFT.js +0 -10
  151. package/dist/chunk-WDZR3QQM.js +0 -135
  152. package/dist/chunk-WLYVJCHV.js +0 -1433
  153. package/dist/chunk-WS7NAFTM.js +0 -1520
  154. package/dist/chunk-X6GDDWQH.js +0 -60
  155. package/dist/chunk-XPDEYNQI.js +0 -102
  156. package/dist/chunk-XRW44VGM.js +0 -58
  157. package/dist/chunk-Y2CM7HXH.js +0 -111
  158. package/dist/chunk-Y7X25LFY.js +0 -15
  159. package/dist/conversation-work-VIOQ5ZF3.js +0 -54
  160. package/dist/db-NGDJV7YH.js +0 -26
  161. package/dist/discovery-DWVDM7UK.js +0 -44
  162. package/dist/experimental-P5FZM4CU.js +0 -12
  163. package/dist/oauth-callback-server-YFY7YEGK.js +0 -243
  164. package/dist/oauth-relay-5XPVRPYB.js +0 -8
  165. package/dist/plugins-YTYJMMJF.js +0 -14
  166. package/dist/runner-ORTNSB2Q.js +0 -522
  167. package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
  168. package/dist/sentry-MB3NL3IR.js +0 -42
  169. package/dist/skills-H376WQOH.js +0 -27
  170. package/dist/spawn-T43WHLOS.js +0 -42
  171. package/dist/task-runner-LBZWF32V.js +0 -36
  172. package/dist/validation-REJLRKI6.js +0 -17
  173. package/dist/work-3KJ3RE76.js +0 -51
  174. package/dist/worker-QFEBU72E.js +0 -24
package/dist/cli/check.js CHANGED
@@ -1,886 +1,593 @@
1
- import {
2
- parseSkillFile
3
- } from "../chunk-A5L27LE4.js";
4
- import "../chunk-WCNQJKFT.js";
5
- import {
6
- parseInlinePluginManifest,
7
- parsePluginManifest
8
- } from "../chunk-7GWQ5S5R.js";
9
- import "../chunk-CEMMLV5L.js";
10
- import "../chunk-N7TMSOZH.js";
11
- import "../chunk-DWPN6SCH.js";
12
- import "../chunk-TXKUKMVO.js";
13
- import {
14
- JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE,
15
- JUNIOR_HEARTBEAT_ROUTE,
16
- LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION
17
- } from "../chunk-WS7NAFTM.js";
18
- import "../chunk-PEQXYSSE.js";
19
- import "../chunk-WDZR3QQM.js";
20
-
21
- // src/cli/check.ts
22
- import fs from "fs/promises";
23
- import path from "path";
24
- import { pathToFileURL } from "url";
25
- var DEFAULT_IO = {
26
- info: console.log,
27
- warn: console.warn,
28
- error: console.error
1
+ import { E as JUNIOR_HEARTBEAT_ROUTE, j as LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION, w as JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE } from "../logging-BMJzdDlP.js";
2
+ import { d as parseInlinePluginManifest, f as parsePluginManifest } from "../registry-BA0__8ai.js";
3
+ import { i as parseSkillFile } from "../skills-DJOlYdBs.js";
4
+ import { pathToFileURL } from "node:url";
5
+ import path from "node:path";
6
+ import fs from "node:fs/promises";
7
+ //#region src/cli/check.ts
8
+ const DEFAULT_IO = {
9
+ info: console.log,
10
+ warn: console.warn,
11
+ error: console.error
29
12
  };
30
- var ANSI = {
31
- reset: "\x1B[0m",
32
- bold: "\x1B[1m",
33
- dim: "\x1B[2m",
34
- green: "\x1B[32m",
35
- yellow: "\x1B[33m",
36
- red: "\x1B[31m",
37
- cyan: "\x1B[36m"
13
+ const ANSI = {
14
+ reset: "\x1B[0m",
15
+ bold: "\x1B[1m",
16
+ dim: "\x1B[2m",
17
+ green: "\x1B[32m",
18
+ yellow: "\x1B[33m",
19
+ red: "\x1B[31m",
20
+ cyan: "\x1B[36m"
38
21
  };
39
22
  function supportsColor(stream) {
40
- if (process.env.NO_COLOR !== void 0) {
41
- return false;
42
- }
43
- if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== "0") {
44
- return true;
45
- }
46
- return Boolean(stream?.isTTY);
47
- }
48
- var COLOR_ENABLED = supportsColor(process.stdout) || supportsColor(process.stderr);
23
+ if (process.env.NO_COLOR !== void 0) return false;
24
+ if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== "0") return true;
25
+ return Boolean(stream?.isTTY);
26
+ }
27
+ const COLOR_ENABLED = supportsColor(process.stdout) || supportsColor(process.stderr);
49
28
  function color(text, ...codes) {
50
- if (!COLOR_ENABLED || codes.length === 0) {
51
- return text;
52
- }
53
- return `${codes.join("")}${text}${ANSI.reset}`;
29
+ if (!COLOR_ENABLED || codes.length === 0) return text;
30
+ return `${codes.join("")}${text}${ANSI.reset}`;
54
31
  }
55
32
  function contentRoot(rootDir, subdir) {
56
- return path.resolve(rootDir, "app", subdir);
33
+ return path.resolve(rootDir, "app", subdir);
57
34
  }
58
35
  async function pathIsDirectory(targetPath) {
59
- try {
60
- return (await fs.stat(targetPath)).isDirectory();
61
- } catch {
62
- return false;
63
- }
36
+ try {
37
+ return (await fs.stat(targetPath)).isDirectory();
38
+ } catch {
39
+ return false;
40
+ }
64
41
  }
65
42
  async function pathIsFile(targetPath) {
66
- try {
67
- return (await fs.stat(targetPath)).isFile();
68
- } catch {
69
- return false;
70
- }
43
+ try {
44
+ return (await fs.stat(targetPath)).isFile();
45
+ } catch {
46
+ return false;
47
+ }
71
48
  }
72
49
  async function readJsonFile(targetPath) {
73
- try {
74
- return JSON.parse(await fs.readFile(targetPath, "utf8"));
75
- } catch {
76
- return void 0;
77
- }
50
+ try {
51
+ return JSON.parse(await fs.readFile(targetPath, "utf8"));
52
+ } catch {
53
+ return;
54
+ }
78
55
  }
79
56
  async function readTextFile(targetPath) {
80
- try {
81
- return await fs.readFile(targetPath, "utf8");
82
- } catch {
83
- return void 0;
84
- }
57
+ try {
58
+ return await fs.readFile(targetPath, "utf8");
59
+ } catch {
60
+ return;
61
+ }
85
62
  }
86
63
  async function readRootPackageJson(rootDir) {
87
- return await readJsonFile(path.join(rootDir, "package.json"));
64
+ return await readJsonFile(path.join(rootDir, "package.json"));
88
65
  }
89
66
  function packageInstallDir(rootDir, packageName) {
90
- return path.join(rootDir, "node_modules", ...packageName.split("/"));
67
+ return path.join(rootDir, "node_modules", ...packageName.split("/"));
91
68
  }
92
69
  function declaredPackages(pkg) {
93
- if (!pkg) {
94
- return [];
95
- }
96
- const packages = /* @__PURE__ */ new Map();
97
- for (const deps of [
98
- pkg.dependencies,
99
- pkg.optionalDependencies,
100
- pkg.devDependencies
101
- ]) {
102
- for (const [name, spec] of Object.entries(deps ?? {})) {
103
- if (!packages.has(name)) {
104
- packages.set(name, spec);
105
- }
106
- }
107
- }
108
- return [...packages.entries()].map(([name, spec]) => ({ name, spec })).sort((left, right) => left.name.localeCompare(right.name));
70
+ if (!pkg) return [];
71
+ const packages = /* @__PURE__ */ new Map();
72
+ for (const deps of [
73
+ pkg.dependencies,
74
+ pkg.optionalDependencies,
75
+ pkg.devDependencies
76
+ ]) for (const [name, spec] of Object.entries(deps ?? {})) if (!packages.has(name)) packages.set(name, spec);
77
+ return [...packages.entries()].map(([name, spec]) => ({
78
+ name,
79
+ spec
80
+ })).sort((left, right) => left.name.localeCompare(right.name));
109
81
  }
110
82
  async function readInstalledPackageVersion(rootDir, packageName) {
111
- return (await readJsonFile(
112
- path.join(packageInstallDir(rootDir, packageName), "package.json")
113
- ))?.version;
83
+ return (await readJsonFile(path.join(packageInstallDir(rootDir, packageName), "package.json")))?.version;
114
84
  }
115
85
  async function packageHasPluginContent(rootDir, packageName) {
116
- const packageDir = packageInstallDir(rootDir, packageName);
117
- return await pathIsFile(path.join(packageDir, "plugin.yaml")) || await pathIsDirectory(path.join(packageDir, "plugins")) || await pathIsDirectory(path.join(packageDir, "skills"));
86
+ const packageDir = packageInstallDir(rootDir, packageName);
87
+ return await pathIsFile(path.join(packageDir, "plugin.yaml")) || await pathIsDirectory(path.join(packageDir, "plugins")) || await pathIsDirectory(path.join(packageDir, "skills"));
118
88
  }
119
89
  function comparableVersion(version) {
120
- return version?.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
90
+ return version?.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
121
91
  }
122
92
  async function collectPackageWarnings(rootDir, packages) {
123
- const warnings = [];
124
- const packageSpecByName = new Map(
125
- packages.map((declaredPackage) => [
126
- declaredPackage.name,
127
- declaredPackage.spec
128
- ])
129
- );
130
- const coreVersion = comparableVersion(
131
- await readInstalledPackageVersion(rootDir, "@sentry/junior") ?? packageSpecByName.get("@sentry/junior")
132
- );
133
- if (!coreVersion) {
134
- return warnings;
135
- }
136
- for (const declaredPackage of packages) {
137
- if (!declaredPackage.name.startsWith("@sentry/junior-")) {
138
- continue;
139
- }
140
- if (!await packageHasPluginContent(rootDir, declaredPackage.name)) {
141
- continue;
142
- }
143
- const pluginVersion = comparableVersion(
144
- await readInstalledPackageVersion(rootDir, declaredPackage.name) ?? declaredPackage.spec
145
- );
146
- if (pluginVersion && pluginVersion !== coreVersion) {
147
- warnings.push(
148
- `${path.join(rootDir, "package.json")}: ${declaredPackage.name} version ${pluginVersion} does not match @sentry/junior version ${coreVersion}`
149
- );
150
- }
151
- }
152
- return warnings;
93
+ const warnings = [];
94
+ const packageSpecByName = new Map(packages.map((declaredPackage) => [declaredPackage.name, declaredPackage.spec]));
95
+ const coreVersion = comparableVersion(await readInstalledPackageVersion(rootDir, "@sentry/junior") ?? packageSpecByName.get("@sentry/junior"));
96
+ if (!coreVersion) return warnings;
97
+ for (const declaredPackage of packages) {
98
+ if (!declaredPackage.name.startsWith("@sentry/junior-")) continue;
99
+ if (!await packageHasPluginContent(rootDir, declaredPackage.name)) continue;
100
+ const pluginVersion = comparableVersion(await readInstalledPackageVersion(rootDir, declaredPackage.name) ?? declaredPackage.spec);
101
+ if (pluginVersion && pluginVersion !== coreVersion) warnings.push(`${path.join(rootDir, "package.json")}: ${declaredPackage.name} version ${pluginVersion} does not match @sentry/junior version ${coreVersion}`);
102
+ }
103
+ return warnings;
153
104
  }
154
105
  async function validateSkillDirectory(skillDir, duplicateNames) {
155
- const skillFile = path.join(skillDir, "SKILL.md");
156
- const errors = [];
157
- const warnings = [];
158
- let raw;
159
- try {
160
- raw = await fs.readFile(skillFile, "utf8");
161
- } catch {
162
- errors.push(`${skillFile}: missing SKILL.md`);
163
- return { skillFile, errors, warnings };
164
- }
165
- const parsed = parseSkillFile(raw, path.basename(skillDir));
166
- if (!parsed.ok) {
167
- errors.push(`${skillFile}: ${parsed.error}`);
168
- return { skillFile, errors, warnings };
169
- }
170
- const name = parsed.skill.name;
171
- const firstSeen = duplicateNames.get(name);
172
- if (firstSeen) {
173
- errors.push(
174
- `${skillFile}: duplicate skill name "${name}" (already defined in ${firstSeen})`
175
- );
176
- } else {
177
- duplicateNames.set(name, skillFile);
178
- }
179
- if (!parsed.skill.body) {
180
- warnings.push(`${skillFile}: no skill instructions after frontmatter`);
181
- }
182
- if (/\b(?:searchTools|searchMcpTools|useTool|callMcpTool|available_tools)\b|<active-mcp-(?:tools|catalogs)>/.test(
183
- parsed.skill.body
184
- )) {
185
- errors.push(
186
- `${skillFile}: skill instructions must not hardcode harness tool-discovery or MCP dispatcher mechanics`
187
- );
188
- }
189
- return { skillFile, skill: parsed.skill, errors, warnings };
106
+ const skillFile = path.join(skillDir, "SKILL.md");
107
+ const errors = [];
108
+ const warnings = [];
109
+ let raw;
110
+ try {
111
+ raw = await fs.readFile(skillFile, "utf8");
112
+ } catch {
113
+ errors.push(`${skillFile}: missing SKILL.md`);
114
+ return {
115
+ skillFile,
116
+ errors,
117
+ warnings
118
+ };
119
+ }
120
+ const parsed = parseSkillFile(raw, path.basename(skillDir));
121
+ if (!parsed.ok) {
122
+ errors.push(`${skillFile}: ${parsed.error}`);
123
+ return {
124
+ skillFile,
125
+ errors,
126
+ warnings
127
+ };
128
+ }
129
+ const name = parsed.skill.name;
130
+ const firstSeen = duplicateNames.get(name);
131
+ if (firstSeen) errors.push(`${skillFile}: duplicate skill name "${name}" (already defined in ${firstSeen})`);
132
+ else duplicateNames.set(name, skillFile);
133
+ if (!parsed.skill.body) warnings.push(`${skillFile}: no skill instructions after frontmatter`);
134
+ if (/\b(?:searchTools|searchMcpTools|useTool|callMcpTool|available_tools)\b|<active-mcp-(?:tools|catalogs)>/.test(parsed.skill.body)) errors.push(`${skillFile}: skill instructions must not hardcode harness tool-discovery or MCP dispatcher mechanics`);
135
+ return {
136
+ skillFile,
137
+ skill: parsed.skill,
138
+ errors,
139
+ warnings
140
+ };
190
141
  }
191
142
  async function validatePluginDirectory(pluginDir, duplicatePluginNames, duplicateProviderDomains) {
192
- const manifestPath = path.join(pluginDir, "plugin.yaml");
193
- try {
194
- const raw = await fs.readFile(manifestPath, "utf8");
195
- const manifest = parsePluginManifest(raw, pluginDir);
196
- return validatePluginManifest(
197
- manifestPath,
198
- manifest,
199
- duplicatePluginNames,
200
- duplicateProviderDomains
201
- );
202
- } catch (error) {
203
- return {
204
- manifestPath,
205
- errors: [
206
- `${manifestPath}: ${error instanceof Error ? error.message : String(error)}`
207
- ]
208
- };
209
- }
143
+ const manifestPath = path.join(pluginDir, "plugin.yaml");
144
+ try {
145
+ const raw = await fs.readFile(manifestPath, "utf8");
146
+ return validatePluginManifest(manifestPath, parsePluginManifest(raw, pluginDir), duplicatePluginNames, duplicateProviderDomains);
147
+ } catch (error) {
148
+ return {
149
+ manifestPath,
150
+ errors: [`${manifestPath}: ${error instanceof Error ? error.message : String(error)}`]
151
+ };
152
+ }
210
153
  }
211
154
  function validatePluginManifest(manifestPath, manifest, duplicatePluginNames, duplicateProviderDomains) {
212
- const errors = [];
213
- const firstSeen = duplicatePluginNames.get(manifest.name);
214
- if (firstSeen) {
215
- errors.push(
216
- `${manifestPath}: duplicate plugin name "${manifest.name}" (already defined in ${firstSeen})`
217
- );
218
- }
219
- const domains = [
220
- .../* @__PURE__ */ new Set([
221
- ...manifest.credentials?.domains ?? [],
222
- ...manifest.domains ?? []
223
- ])
224
- ];
225
- for (const domain of domains) {
226
- const firstDomainSeen = duplicateProviderDomains.get(domain);
227
- if (firstDomainSeen) {
228
- errors.push(
229
- `${manifestPath}: duplicate provider domain "${domain}" (already defined in ${firstDomainSeen})`
230
- );
231
- }
232
- }
233
- if (errors.length > 0) {
234
- return { manifestPath, manifest, errors };
235
- }
236
- duplicatePluginNames.set(manifest.name, manifestPath);
237
- for (const domain of domains) {
238
- duplicateProviderDomains.set(domain, manifestPath);
239
- }
240
- return { manifestPath, manifest, errors: [] };
155
+ const errors = [];
156
+ const firstSeen = duplicatePluginNames.get(manifest.name);
157
+ if (firstSeen) errors.push(`${manifestPath}: duplicate plugin name "${manifest.name}" (already defined in ${firstSeen})`);
158
+ const domains = [.../* @__PURE__ */ new Set([...manifest.credentials?.domains ?? [], ...manifest.domains ?? []])];
159
+ for (const domain of domains) {
160
+ const firstDomainSeen = duplicateProviderDomains.get(domain);
161
+ if (firstDomainSeen) errors.push(`${manifestPath}: duplicate provider domain "${domain}" (already defined in ${firstDomainSeen})`);
162
+ }
163
+ if (errors.length > 0) return {
164
+ manifestPath,
165
+ manifest,
166
+ errors
167
+ };
168
+ duplicatePluginNames.set(manifest.name, manifestPath);
169
+ for (const domain of domains) duplicateProviderDomains.set(domain, manifestPath);
170
+ return {
171
+ manifestPath,
172
+ manifest,
173
+ errors: []
174
+ };
241
175
  }
242
176
  function packageExportsEntryPath(exportsValue) {
243
- if (typeof exportsValue === "string") {
244
- return exportsValue;
245
- }
246
- if (!isRecord(exportsValue)) {
247
- return void 0;
248
- }
249
- const rootExport = exportsValue["."] ?? exportsValue;
250
- if (typeof rootExport === "string") {
251
- return rootExport;
252
- }
253
- if (!isRecord(rootExport)) {
254
- return void 0;
255
- }
256
- for (const condition of ["default", "import", "node"]) {
257
- const target = rootExport[condition];
258
- if (typeof target === "string") {
259
- return target;
260
- }
261
- }
262
- return void 0;
177
+ if (typeof exportsValue === "string") return exportsValue;
178
+ if (!isRecord(exportsValue)) return;
179
+ const rootExport = exportsValue["."] ?? exportsValue;
180
+ if (typeof rootExport === "string") return rootExport;
181
+ if (!isRecord(rootExport)) return;
182
+ for (const condition of [
183
+ "default",
184
+ "import",
185
+ "node"
186
+ ]) {
187
+ const target = rootExport[condition];
188
+ if (typeof target === "string") return target;
189
+ }
263
190
  }
264
191
  async function resolvePackageEntryPath(packageDir) {
265
- const packageJson = await readJsonFile(
266
- path.join(packageDir, "package.json")
267
- );
268
- const entry = packageExportsEntryPath(packageJson?.exports) ?? packageJson?.main;
269
- const candidates = [
270
- ...entry ? [entry] : [],
271
- "./index.js",
272
- "./dist/index.js"
273
- ];
274
- for (const candidate of candidates) {
275
- if (path.isAbsolute(candidate) || candidate.startsWith("../")) {
276
- continue;
277
- }
278
- const normalizedCandidate = candidate.startsWith("./") ? candidate : `./${candidate}`;
279
- const entryPath = path.resolve(packageDir, normalizedCandidate);
280
- if (await pathIsFile(entryPath)) {
281
- return entryPath;
282
- }
283
- }
284
- return void 0;
192
+ const packageJson = await readJsonFile(path.join(packageDir, "package.json"));
193
+ const entry = packageExportsEntryPath(packageJson?.exports) ?? packageJson?.main;
194
+ const candidates = [
195
+ ...entry ? [entry] : [],
196
+ "./index.js",
197
+ "./dist/index.js"
198
+ ];
199
+ for (const candidate of candidates) {
200
+ if (path.isAbsolute(candidate) || candidate.startsWith("../")) continue;
201
+ const normalizedCandidate = candidate.startsWith("./") ? candidate : `./${candidate}`;
202
+ const entryPath = path.resolve(packageDir, normalizedCandidate);
203
+ if (await pathIsFile(entryPath)) return entryPath;
204
+ }
285
205
  }
286
206
  function isPluginFactoryExport(name, value) {
287
- return typeof value === "function" && (name === "default" || name.endsWith("Plugin"));
207
+ return typeof value === "function" && (name === "default" || name.endsWith("Plugin"));
288
208
  }
289
209
  function isPluginRegistrationLike(value) {
290
- return isRecord(value) && isRecord(value.manifest);
210
+ return isRecord(value) && isRecord(value.manifest);
291
211
  }
292
212
  async function collectPackageJsPluginSources(packageDir, packageName) {
293
- const entryPath = await resolvePackageEntryPath(packageDir);
294
- if (!entryPath) {
295
- return [];
296
- }
297
- let moduleExports;
298
- try {
299
- moduleExports = await import(pathToFileURL(entryPath).href);
300
- } catch {
301
- return [];
302
- }
303
- const pluginSources = [];
304
- for (const [exportName, exportedValue] of Object.entries(moduleExports).sort(
305
- ([left], [right]) => left.localeCompare(right)
306
- )) {
307
- if (!isPluginFactoryExport(exportName, exportedValue)) {
308
- continue;
309
- }
310
- let registration;
311
- try {
312
- registration = await exportedValue();
313
- } catch {
314
- continue;
315
- }
316
- if (!isPluginRegistrationLike(registration)) {
317
- continue;
318
- }
319
- const manifestPath = `${entryPath}#${exportName}`;
320
- try {
321
- pluginSources.push({
322
- pluginDir: packageDir,
323
- packageName,
324
- manifestPath,
325
- manifest: parseInlinePluginManifest(registration.manifest, packageDir)
326
- });
327
- } catch (error) {
328
- pluginSources.push({
329
- pluginDir: packageDir,
330
- packageName,
331
- manifestPath,
332
- errors: [
333
- `${manifestPath}: ${error instanceof Error ? error.message : String(error)}`
334
- ]
335
- });
336
- }
337
- }
338
- return pluginSources;
213
+ const entryPath = await resolvePackageEntryPath(packageDir);
214
+ if (!entryPath) return [];
215
+ let moduleExports;
216
+ try {
217
+ moduleExports = await import(pathToFileURL(entryPath).href);
218
+ } catch {
219
+ return [];
220
+ }
221
+ const pluginSources = [];
222
+ for (const [exportName, exportedValue] of Object.entries(moduleExports).sort(([left], [right]) => left.localeCompare(right))) {
223
+ if (!isPluginFactoryExport(exportName, exportedValue)) continue;
224
+ let registration;
225
+ try {
226
+ registration = await exportedValue();
227
+ } catch {
228
+ continue;
229
+ }
230
+ if (!isPluginRegistrationLike(registration)) continue;
231
+ const manifestPath = `${entryPath}#${exportName}`;
232
+ try {
233
+ pluginSources.push({
234
+ pluginDir: packageDir,
235
+ packageName,
236
+ manifestPath,
237
+ manifest: parseInlinePluginManifest(registration.manifest, packageDir)
238
+ });
239
+ } catch (error) {
240
+ pluginSources.push({
241
+ pluginDir: packageDir,
242
+ packageName,
243
+ manifestPath,
244
+ errors: [`${manifestPath}: ${error instanceof Error ? error.message : String(error)}`]
245
+ });
246
+ }
247
+ }
248
+ return pluginSources;
339
249
  }
340
250
  async function collectPluginDirectories(rootDir) {
341
- const pluginDirs = [];
342
- let entries;
343
- try {
344
- entries = await fs.readdir(contentRoot(rootDir, "plugins"), {
345
- withFileTypes: true
346
- });
347
- } catch {
348
- return pluginDirs;
349
- }
350
- for (const entry of entries) {
351
- if (!entry.isDirectory()) {
352
- continue;
353
- }
354
- const pluginDir = path.join(contentRoot(rootDir, "plugins"), entry.name);
355
- if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) {
356
- pluginDirs.push(pluginDir);
357
- }
358
- }
359
- return pluginDirs.sort((left, right) => left.localeCompare(right));
251
+ const pluginDirs = [];
252
+ let entries;
253
+ try {
254
+ entries = await fs.readdir(contentRoot(rootDir, "plugins"), { withFileTypes: true });
255
+ } catch {
256
+ return pluginDirs;
257
+ }
258
+ for (const entry of entries) {
259
+ if (!entry.isDirectory()) continue;
260
+ const pluginDir = path.join(contentRoot(rootDir, "plugins"), entry.name);
261
+ if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) pluginDirs.push(pluginDir);
262
+ }
263
+ return pluginDirs.sort((left, right) => left.localeCompare(right));
360
264
  }
361
265
  async function collectPackagedContent(rootDir, packages) {
362
- const pluginSources = [];
363
- const skillRoots = [];
364
- for (const declaredPackage of packages) {
365
- const packageDir = packageInstallDir(rootDir, declaredPackage.name);
366
- if (!await pathIsDirectory(packageDir)) {
367
- continue;
368
- }
369
- const rootManifestPath = path.join(packageDir, "plugin.yaml");
370
- const hasRootManifest = await pathIsFile(rootManifestPath);
371
- const packageSkillsRoot = path.join(packageDir, "skills");
372
- const hasPackageSkills = await pathIsDirectory(packageSkillsRoot);
373
- if (hasRootManifest) {
374
- pluginSources.push({
375
- pluginDir: packageDir,
376
- manifestPath: rootManifestPath,
377
- packageName: declaredPackage.name
378
- });
379
- } else if (hasPackageSkills) {
380
- const jsPluginSources = await collectPackageJsPluginSources(
381
- packageDir,
382
- declaredPackage.name
383
- );
384
- if (jsPluginSources.length > 0) {
385
- pluginSources.push(...jsPluginSources);
386
- } else {
387
- skillRoots.push({
388
- root: packageSkillsRoot,
389
- packageName: declaredPackage.name
390
- });
391
- }
392
- }
393
- const nestedPluginsRoot = path.join(packageDir, "plugins");
394
- let nestedEntries;
395
- try {
396
- nestedEntries = await fs.readdir(nestedPluginsRoot, {
397
- withFileTypes: true
398
- });
399
- } catch {
400
- continue;
401
- }
402
- for (const entry of nestedEntries) {
403
- if (!entry.isDirectory()) {
404
- continue;
405
- }
406
- const pluginDir = path.join(nestedPluginsRoot, entry.name);
407
- if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) {
408
- pluginSources.push({
409
- pluginDir,
410
- manifestPath: path.join(pluginDir, "plugin.yaml"),
411
- packageName: declaredPackage.name
412
- });
413
- }
414
- }
415
- }
416
- return {
417
- pluginSources: pluginSources.sort(
418
- (left, right) => `${left.packageName}:${left.pluginDir}`.localeCompare(
419
- `${right.packageName}:${right.pluginDir}`
420
- )
421
- ),
422
- skillRoots: skillRoots.sort(
423
- (left, right) => `${left.packageName}:${left.root}`.localeCompare(
424
- `${right.packageName}:${right.root}`
425
- )
426
- )
427
- };
266
+ const pluginSources = [];
267
+ const skillRoots = [];
268
+ for (const declaredPackage of packages) {
269
+ const packageDir = packageInstallDir(rootDir, declaredPackage.name);
270
+ if (!await pathIsDirectory(packageDir)) continue;
271
+ const rootManifestPath = path.join(packageDir, "plugin.yaml");
272
+ const hasRootManifest = await pathIsFile(rootManifestPath);
273
+ const packageSkillsRoot = path.join(packageDir, "skills");
274
+ const hasPackageSkills = await pathIsDirectory(packageSkillsRoot);
275
+ if (hasRootManifest) pluginSources.push({
276
+ pluginDir: packageDir,
277
+ manifestPath: rootManifestPath,
278
+ packageName: declaredPackage.name
279
+ });
280
+ else if (hasPackageSkills) {
281
+ const jsPluginSources = await collectPackageJsPluginSources(packageDir, declaredPackage.name);
282
+ if (jsPluginSources.length > 0) pluginSources.push(...jsPluginSources);
283
+ else skillRoots.push({
284
+ root: packageSkillsRoot,
285
+ packageName: declaredPackage.name
286
+ });
287
+ }
288
+ const nestedPluginsRoot = path.join(packageDir, "plugins");
289
+ let nestedEntries;
290
+ try {
291
+ nestedEntries = await fs.readdir(nestedPluginsRoot, { withFileTypes: true });
292
+ } catch {
293
+ continue;
294
+ }
295
+ for (const entry of nestedEntries) {
296
+ if (!entry.isDirectory()) continue;
297
+ const pluginDir = path.join(nestedPluginsRoot, entry.name);
298
+ if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) pluginSources.push({
299
+ pluginDir,
300
+ manifestPath: path.join(pluginDir, "plugin.yaml"),
301
+ packageName: declaredPackage.name
302
+ });
303
+ }
304
+ }
305
+ return {
306
+ pluginSources: pluginSources.sort((left, right) => `${left.packageName}:${left.pluginDir}`.localeCompare(`${right.packageName}:${right.pluginDir}`)),
307
+ skillRoots: skillRoots.sort((left, right) => `${left.packageName}:${left.root}`.localeCompare(`${right.packageName}:${right.root}`))
308
+ };
428
309
  }
429
310
  async function collectSkillDirectories(root) {
430
- const skillDirs = [];
431
- let entries;
432
- try {
433
- entries = await fs.readdir(root, { withFileTypes: true });
434
- } catch {
435
- return skillDirs;
436
- }
437
- for (const entry of entries) {
438
- if (entry.isDirectory()) {
439
- skillDirs.push(path.join(root, entry.name));
440
- }
441
- }
442
- return skillDirs.sort((left, right) => left.localeCompare(right));
311
+ const skillDirs = [];
312
+ let entries;
313
+ try {
314
+ entries = await fs.readdir(root, { withFileTypes: true });
315
+ } catch {
316
+ return skillDirs;
317
+ }
318
+ for (const entry of entries) if (entry.isDirectory()) skillDirs.push(path.join(root, entry.name));
319
+ return skillDirs.sort((left, right) => left.localeCompare(right));
443
320
  }
444
321
  function formatDisplayPath(rootDir, targetPath) {
445
- const relativePath = path.relative(rootDir, targetPath);
446
- const displayPath = relativePath.length > 0 && !relativePath.startsWith("..") && !path.isAbsolute(relativePath) ? relativePath : targetPath;
447
- return displayPath.split(path.sep).join("/");
322
+ const relativePath = path.relative(rootDir, targetPath);
323
+ return (relativePath.length > 0 && !relativePath.startsWith("..") && !path.isAbsolute(relativePath) ? relativePath : targetPath).split(path.sep).join("/");
448
324
  }
449
325
  function formatStatus(errorCount, warningCount) {
450
- if (errorCount > 0) {
451
- return "error";
452
- }
453
- if (warningCount > 0) {
454
- return "warn";
455
- }
456
- return "ok";
326
+ if (errorCount > 0) return "error";
327
+ if (warningCount > 0) return "warn";
328
+ return "ok";
457
329
  }
458
330
  function statusIcon(status) {
459
- switch (status) {
460
- case "ok":
461
- return color("\u2713", ANSI.green, ANSI.bold);
462
- case "warn":
463
- return color("\u26A0", ANSI.yellow, ANSI.bold);
464
- case "error":
465
- return color("\u2716", ANSI.red, ANSI.bold);
466
- }
331
+ switch (status) {
332
+ case "ok": return color("✓", ANSI.green, ANSI.bold);
333
+ case "warn": return color("⚠", ANSI.yellow, ANSI.bold);
334
+ case "error": return color("✖", ANSI.red, ANSI.bold);
335
+ }
467
336
  }
468
337
  function formatHeading(status, label) {
469
- const styledLabel = status === "ok" ? color(label, ANSI.bold) : status === "warn" ? color(label, ANSI.bold, ANSI.yellow) : color(label, ANSI.bold, ANSI.red);
470
- return `${statusIcon(status)} ${styledLabel}`;
338
+ const styledLabel = status === "ok" ? color(label, ANSI.bold) : status === "warn" ? color(label, ANSI.bold, ANSI.yellow) : color(label, ANSI.bold, ANSI.red);
339
+ return `${statusIcon(status)} ${styledLabel}`;
471
340
  }
472
341
  function reportSkillResult(result, io, indent, isLast) {
473
- const status = formatStatus(result.errors.length, result.warnings.length);
474
- const skillName = result.skill?.name ?? path.basename(path.dirname(result.skillFile));
475
- const branch = isLast ? "\u2514\u2500" : "\u251C\u2500";
476
- io.info(`${indent}${branch} ${formatHeading(status, `skill ${skillName}`)}`);
342
+ const status = formatStatus(result.errors.length, result.warnings.length);
343
+ const skillName = result.skill?.name ?? path.basename(path.dirname(result.skillFile));
344
+ const branch = isLast ? "└─" : "├─";
345
+ io.info(`${indent}${branch} ${formatHeading(status, `skill ${skillName}`)}`);
477
346
  }
478
347
  function reportPluginResult(result, io) {
479
- const skillErrorCount = result.skillResults.reduce(
480
- (count, skillResult) => count + skillResult.errors.length,
481
- 0
482
- );
483
- const skillWarningCount = result.skillResults.reduce(
484
- (count, skillResult) => count + skillResult.warnings.length,
485
- 0
486
- );
487
- const status = formatStatus(
488
- result.errors.length + skillErrorCount,
489
- skillWarningCount
490
- );
491
- const pluginName = result.manifest?.name ?? path.basename(result.pluginDir);
492
- const label = result.packageName ? `packaged plugin ${pluginName} (${result.packageName})` : `plugin ${pluginName}`;
493
- io.info(formatHeading(status, label));
494
- for (const [index, skillResult] of result.skillResults.entries()) {
495
- reportSkillResult(
496
- skillResult,
497
- io,
498
- " ",
499
- index === result.skillResults.length - 1
500
- );
501
- }
348
+ const skillErrorCount = result.skillResults.reduce((count, skillResult) => count + skillResult.errors.length, 0);
349
+ const skillWarningCount = result.skillResults.reduce((count, skillResult) => count + skillResult.warnings.length, 0);
350
+ const status = formatStatus(result.errors.length + skillErrorCount, skillWarningCount);
351
+ const pluginName = result.manifest?.name ?? path.basename(result.pluginDir);
352
+ const label = result.packageName ? `packaged plugin ${pluginName} (${result.packageName})` : `plugin ${pluginName}`;
353
+ io.info(formatHeading(status, label));
354
+ for (const [index, skillResult] of result.skillResults.entries()) reportSkillResult(skillResult, io, " ", index === result.skillResults.length - 1);
502
355
  }
503
356
  function reportSkillGroup(label, skillResults, io) {
504
- const errorCount = skillResults.reduce(
505
- (count, skillResult) => count + skillResult.errors.length,
506
- 0
507
- );
508
- const warningCount = skillResults.reduce(
509
- (count, skillResult) => count + skillResult.warnings.length,
510
- 0
511
- );
512
- const status = formatStatus(errorCount, warningCount);
513
- io.info(formatHeading(status, label));
514
- for (const [index, skillResult] of skillResults.entries()) {
515
- reportSkillResult(skillResult, io, " ", index === skillResults.length - 1);
516
- }
357
+ const status = formatStatus(skillResults.reduce((count, skillResult) => count + skillResult.errors.length, 0), skillResults.reduce((count, skillResult) => count + skillResult.warnings.length, 0));
358
+ io.info(formatHeading(status, label));
359
+ for (const [index, skillResult] of skillResults.entries()) reportSkillResult(skillResult, io, " ", index === skillResults.length - 1);
517
360
  }
518
361
  function reportAppSkills(skillResults, io) {
519
- reportSkillGroup("app skills", skillResults, io);
362
+ reportSkillGroup("app skills", skillResults, io);
520
363
  }
521
364
  function isRecord(value) {
522
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
365
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
523
366
  }
524
367
  async function hasJuniorDeploymentMarkers(rootDir, packages, nitroConfig) {
525
- if (packages.some(
526
- (declaredPackage) => declaredPackage.name === "@sentry/junior"
527
- )) {
528
- return true;
529
- }
530
- for (const source of [
531
- nitroConfig,
532
- await readTextFile(path.join(rootDir, "server.ts")),
533
- await readTextFile(path.join(rootDir, "server.js"))
534
- ]) {
535
- if (source?.includes("@sentry/junior") || /\bjuniorNitro\s*\(/.test(source ?? "")) {
536
- return true;
537
- }
538
- }
539
- const appDir = path.resolve(rootDir, "app");
540
- return await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
368
+ if (packages.some((declaredPackage) => declaredPackage.name === "@sentry/junior")) return true;
369
+ for (const source of [
370
+ nitroConfig,
371
+ await readTextFile(path.join(rootDir, "server.ts")),
372
+ await readTextFile(path.join(rootDir, "server.js"))
373
+ ]) if (source?.includes("@sentry/junior") || /\bjuniorNitro\s*\(/.test(source ?? "")) return true;
374
+ const appDir = path.resolve(rootDir, "app");
375
+ return await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
541
376
  }
542
377
  async function validateDeploymentConfig(rootDir, packages) {
543
- const errors = [];
544
- const warnings = [];
545
- let checked = false;
546
- const nitroConfigPath = path.join(rootDir, "nitro.config.ts");
547
- const nitroConfig = await readTextFile(nitroConfigPath);
548
- const hasJuniorMarkers = await hasJuniorDeploymentMarkers(
549
- rootDir,
550
- packages,
551
- nitroConfig
552
- );
553
- if (nitroConfig !== void 0 && hasJuniorMarkers) {
554
- checked = true;
555
- if (!/\bjuniorNitro\s*\(/.test(nitroConfig)) {
556
- errors.push(
557
- `${nitroConfigPath}: missing juniorNitro(). The Nitro module emits Junior's Vercel queue trigger and heartbeat cron into the deployed build output.`
558
- );
559
- }
560
- }
561
- const vercelConfigPath = path.join(rootDir, "vercel.json");
562
- const vercelConfigText = await readTextFile(vercelConfigPath);
563
- if (vercelConfigText === void 0) {
564
- return { checked, errors, warnings };
565
- }
566
- const containsLegacyJuniorQueuePath = vercelConfigText.includes(
567
- LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION
568
- );
569
- if (!hasJuniorMarkers && !containsLegacyJuniorQueuePath) {
570
- return { checked, errors, warnings };
571
- }
572
- let vercelConfig;
573
- try {
574
- vercelConfig = JSON.parse(vercelConfigText);
575
- } catch (error) {
576
- errors.push(
577
- `${vercelConfigPath}: invalid JSON (${error instanceof Error ? error.message : String(error)})`
578
- );
579
- return { checked, errors, warnings };
580
- }
581
- if (hasJuniorMarkers && vercelConfig.framework !== void 0 && vercelConfig.framework !== "nitro") {
582
- checked = true;
583
- errors.push(
584
- `${vercelConfigPath}: framework must be "nitro" for Junior's Vercel deployment wiring.`
585
- );
586
- }
587
- if (isRecord(vercelConfig.functions)) {
588
- const legacyFunctionConfig = vercelConfig.functions[LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION];
589
- if (legacyFunctionConfig !== void 0) {
590
- checked = true;
591
- errors.push(
592
- `${vercelConfigPath}: functions.${LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION} targets a source file that Nitro does not deploy. Remove it; juniorNitro() emits the ${JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE} queue trigger through Nitro functionRules.`
593
- );
594
- }
595
- }
596
- if (hasJuniorMarkers && Array.isArray(vercelConfig.crons)) {
597
- const hasHeartbeatCron = vercelConfig.crons.some(
598
- (cron) => isRecord(cron) && cron.path === JUNIOR_HEARTBEAT_ROUTE
599
- );
600
- if (hasHeartbeatCron) {
601
- checked = true;
602
- warnings.push(
603
- `${vercelConfigPath}: ${JUNIOR_HEARTBEAT_ROUTE} cron is now emitted by juniorNitro() into Nitro's Vercel Build Output config. Remove the root cron entry to avoid deployment drift.`
604
- );
605
- }
606
- }
607
- return { checked, errors, warnings };
378
+ const errors = [];
379
+ const warnings = [];
380
+ let checked = false;
381
+ const nitroConfigPath = path.join(rootDir, "nitro.config.ts");
382
+ const nitroConfig = await readTextFile(nitroConfigPath);
383
+ const hasJuniorMarkers = await hasJuniorDeploymentMarkers(rootDir, packages, nitroConfig);
384
+ if (nitroConfig !== void 0 && hasJuniorMarkers) {
385
+ checked = true;
386
+ if (!/\bjuniorNitro\s*\(/.test(nitroConfig)) errors.push(`${nitroConfigPath}: missing juniorNitro(). The Nitro module emits Junior's Vercel queue trigger and heartbeat cron into the deployed build output.`);
387
+ }
388
+ const vercelConfigPath = path.join(rootDir, "vercel.json");
389
+ const vercelConfigText = await readTextFile(vercelConfigPath);
390
+ if (vercelConfigText === void 0) return {
391
+ checked,
392
+ errors,
393
+ warnings
394
+ };
395
+ const containsLegacyJuniorQueuePath = vercelConfigText.includes(LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION);
396
+ if (!hasJuniorMarkers && !containsLegacyJuniorQueuePath) return {
397
+ checked,
398
+ errors,
399
+ warnings
400
+ };
401
+ let vercelConfig;
402
+ try {
403
+ vercelConfig = JSON.parse(vercelConfigText);
404
+ } catch (error) {
405
+ errors.push(`${vercelConfigPath}: invalid JSON (${error instanceof Error ? error.message : String(error)})`);
406
+ return {
407
+ checked,
408
+ errors,
409
+ warnings
410
+ };
411
+ }
412
+ if (hasJuniorMarkers && vercelConfig.framework !== void 0 && vercelConfig.framework !== "nitro") {
413
+ checked = true;
414
+ errors.push(`${vercelConfigPath}: framework must be "nitro" for Junior's Vercel deployment wiring.`);
415
+ }
416
+ if (isRecord(vercelConfig.functions)) {
417
+ if (vercelConfig.functions["api/internal/agent/continue.ts"] !== void 0) {
418
+ checked = true;
419
+ errors.push(`${vercelConfigPath}: functions.${LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION} targets a source file that Nitro does not deploy. Remove it; juniorNitro() emits the ${JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE} queue trigger through Nitro functionRules.`);
420
+ }
421
+ }
422
+ if (hasJuniorMarkers && Array.isArray(vercelConfig.crons)) {
423
+ if (vercelConfig.crons.some((cron) => isRecord(cron) && cron.path === "/api/internal/heartbeat")) {
424
+ checked = true;
425
+ warnings.push(`${vercelConfigPath}: ${JUNIOR_HEARTBEAT_ROUTE} cron is now emitted by juniorNitro() into Nitro's Vercel Build Output config. Remove the root cron entry to avoid deployment drift.`);
426
+ }
427
+ }
428
+ return {
429
+ checked,
430
+ errors,
431
+ warnings
432
+ };
608
433
  }
609
434
  async function validateAppSourceFiles(rootDir, registeredConfigKeys) {
610
- const errors = [];
611
- const warnings = [];
612
- for (const fileName of ["server.ts", "server.js", "nitro.config.ts"]) {
613
- const sourcePath = path.join(rootDir, fileName);
614
- let source;
615
- try {
616
- source = await fs.readFile(sourcePath, "utf8");
617
- } catch {
618
- continue;
619
- }
620
- if (/\bpluginPackages\s*:/.test(source)) {
621
- errors.push(
622
- `${sourcePath}: pluginPackages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`
623
- );
624
- }
625
- if (/\bplugins\s*:\s*\{\s*packages\s*:/.test(source)) {
626
- errors.push(
627
- `${sourcePath}: plugins.packages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`
628
- );
629
- }
630
- for (const defaultsBlock of source.matchAll(
631
- /\bconfigDefaults\s*:\s*\{([\s\S]*?)\}/g
632
- )) {
633
- const block = defaultsBlock[1] ?? "";
634
- for (const keyMatch of block.matchAll(/["']([^"']+)["']\s*:/g)) {
635
- const key = keyMatch[1];
636
- if (key && !registeredConfigKeys.has(key)) {
637
- warnings.push(
638
- `${sourcePath}: configDefaults key "${key}" is not a registered plugin config key`
639
- );
640
- }
641
- }
642
- }
643
- }
644
- return { errors, warnings };
435
+ const errors = [];
436
+ const warnings = [];
437
+ for (const fileName of [
438
+ "server.ts",
439
+ "server.js",
440
+ "nitro.config.ts"
441
+ ]) {
442
+ const sourcePath = path.join(rootDir, fileName);
443
+ let source;
444
+ try {
445
+ source = await fs.readFile(sourcePath, "utf8");
446
+ } catch {
447
+ continue;
448
+ }
449
+ if (/\bpluginPackages\s*:/.test(source)) errors.push(`${sourcePath}: pluginPackages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`);
450
+ if (/\bplugins\s*:\s*\{\s*packages\s*:/.test(source)) errors.push(`${sourcePath}: plugins.packages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`);
451
+ for (const defaultsBlock of source.matchAll(/\bconfigDefaults\s*:\s*\{([\s\S]*?)\}/g)) {
452
+ const block = defaultsBlock[1] ?? "";
453
+ for (const keyMatch of block.matchAll(/["']([^"']+)["']\s*:/g)) {
454
+ const key = keyMatch[1];
455
+ if (key && !registeredConfigKeys.has(key)) warnings.push(`${sourcePath}: configDefaults key "${key}" is not a registered plugin config key`);
456
+ }
457
+ }
458
+ }
459
+ return {
460
+ errors,
461
+ warnings
462
+ };
645
463
  }
646
464
  async function validateAppFiles(appDir) {
647
- const errors = [];
648
- const warnings = [];
649
- if (await pathIsFile(path.join(appDir, "ABOUT.md"))) {
650
- errors.push(
651
- `${path.join(appDir, "ABOUT.md")}: ABOUT.md is no longer supported. Rename to WORLD.md (operational context) and DESCRIPTION.md (user-facing description).`
652
- );
653
- }
654
- if (!await pathIsFile(path.join(appDir, "SOUL.md"))) {
655
- warnings.push(`${path.join(appDir, "SOUL.md")}: missing SOUL.md`);
656
- }
657
- if (!await pathIsFile(path.join(appDir, "WORLD.md"))) {
658
- warnings.push(`${path.join(appDir, "WORLD.md")}: missing WORLD.md`);
659
- }
660
- if (!await pathIsFile(path.join(appDir, "DESCRIPTION.md"))) {
661
- warnings.push(
662
- `${path.join(appDir, "DESCRIPTION.md")}: missing DESCRIPTION.md`
663
- );
664
- }
665
- return { errors, warnings };
465
+ const errors = [];
466
+ const warnings = [];
467
+ if (await pathIsFile(path.join(appDir, "ABOUT.md"))) errors.push(`${path.join(appDir, "ABOUT.md")}: ABOUT.md is no longer supported. Rename to WORLD.md (operational context) and DESCRIPTION.md (user-facing description).`);
468
+ if (!await pathIsFile(path.join(appDir, "SOUL.md"))) warnings.push(`${path.join(appDir, "SOUL.md")}: missing SOUL.md`);
469
+ if (!await pathIsFile(path.join(appDir, "WORLD.md"))) warnings.push(`${path.join(appDir, "WORLD.md")}: missing WORLD.md`);
470
+ if (!await pathIsFile(path.join(appDir, "DESCRIPTION.md"))) warnings.push(`${path.join(appDir, "DESCRIPTION.md")}: missing DESCRIPTION.md`);
471
+ return {
472
+ errors,
473
+ warnings
474
+ };
666
475
  }
667
476
  async function hasJuniorAppMarkers(appDir) {
668
- for (const fileName of [
669
- "SOUL.md",
670
- "WORLD.md",
671
- "DESCRIPTION.md",
672
- "ABOUT.md"
673
- ]) {
674
- if (await pathIsFile(path.join(appDir, fileName))) {
675
- return true;
676
- }
677
- }
678
- for (const dirName of ["skills", "plugins"]) {
679
- if (await pathIsDirectory(path.join(appDir, dirName))) {
680
- return true;
681
- }
682
- }
683
- return false;
477
+ for (const fileName of [
478
+ "SOUL.md",
479
+ "WORLD.md",
480
+ "DESCRIPTION.md",
481
+ "ABOUT.md"
482
+ ]) if (await pathIsFile(path.join(appDir, fileName))) return true;
483
+ for (const dirName of ["skills", "plugins"]) if (await pathIsDirectory(path.join(appDir, dirName))) return true;
484
+ return false;
684
485
  }
685
486
  async function runCheck(rootDir = process.cwd(), io = DEFAULT_IO) {
686
- const resolvedRoot = path.resolve(rootDir);
687
- if (!await pathIsDirectory(resolvedRoot)) {
688
- throw new Error(
689
- `validation root does not exist or is not a directory: ${resolvedRoot}`
690
- );
691
- }
692
- const rootPackageJson = await readRootPackageJson(resolvedRoot);
693
- const packages = declaredPackages(rootPackageJson);
694
- const packageWarnings = await collectPackageWarnings(resolvedRoot, packages);
695
- const packagedContent = await collectPackagedContent(resolvedRoot, packages);
696
- const appPluginDirs = await collectPluginDirectories(resolvedRoot);
697
- const packagedPluginSources = packagedContent.pluginSources;
698
- const pluginSources = [
699
- ...appPluginDirs.map((pluginDir) => ({
700
- pluginDir,
701
- manifestPath: path.join(pluginDir, "plugin.yaml")
702
- })),
703
- ...packagedPluginSources
704
- ];
705
- const appSkillsRoot = contentRoot(resolvedRoot, "skills");
706
- const appSkillDirs = await collectSkillDirectories(appSkillsRoot);
707
- const pluginSkillDirs = /* @__PURE__ */ new Map();
708
- for (const { pluginDir } of pluginSources) {
709
- pluginSkillDirs.set(
710
- pluginDir,
711
- await collectSkillDirectories(path.join(pluginDir, "skills"))
712
- );
713
- }
714
- const packagedSkillDirsByPackage = /* @__PURE__ */ new Map();
715
- for (const skillRoot of packagedContent.skillRoots) {
716
- packagedSkillDirsByPackage.set(
717
- skillRoot.packageName,
718
- await collectSkillDirectories(skillRoot.root)
719
- );
720
- }
721
- const packagedStandaloneSkillDirs = [
722
- ...packagedSkillDirsByPackage.values()
723
- ].flat();
724
- const appAndLocalPluginSkillDirs = [
725
- ...appSkillDirs,
726
- ...appPluginDirs.flatMap(
727
- (pluginDir) => pluginSkillDirs.get(pluginDir) ?? []
728
- )
729
- ].sort((left, right) => left.localeCompare(right));
730
- const packagedSkillDirs = [
731
- ...packagedPluginSources.flatMap(
732
- ({ pluginDir }) => pluginSkillDirs.get(pluginDir) ?? []
733
- ),
734
- ...packagedStandaloneSkillDirs
735
- ].sort((left, right) => left.localeCompare(right));
736
- const skillDirs = [...appAndLocalPluginSkillDirs, ...packagedSkillDirs].sort(
737
- (left, right) => left.localeCompare(right)
738
- );
739
- const duplicateSkillNames = /* @__PURE__ */ new Map();
740
- const duplicatePluginNames = /* @__PURE__ */ new Map();
741
- const duplicateProviderDomains = /* @__PURE__ */ new Map();
742
- const duplicatePackagedSkillNames = /* @__PURE__ */ new Map();
743
- const duplicatePackagedPluginNames = /* @__PURE__ */ new Map();
744
- const duplicatePackagedProviderDomains = /* @__PURE__ */ new Map();
745
- const warnings = [];
746
- const errors = [];
747
- const pluginResults = [];
748
- const skillResultsByDir = /* @__PURE__ */ new Map();
749
- warnings.push(...packageWarnings);
750
- const deploymentConfigResult = await validateDeploymentConfig(
751
- resolvedRoot,
752
- packages
753
- );
754
- warnings.push(...deploymentConfigResult.warnings);
755
- errors.push(...deploymentConfigResult.errors);
756
- for (const source of pluginSources) {
757
- const { pluginDir, packageName } = source;
758
- const pluginNameMap = packageName ? duplicatePackagedPluginNames : duplicatePluginNames;
759
- const providerDomainMap = packageName ? duplicatePackagedProviderDomains : duplicateProviderDomains;
760
- const result = source.errors ? {
761
- manifestPath: source.manifestPath,
762
- ...source.manifest ? { manifest: source.manifest } : void 0,
763
- errors: source.errors
764
- } : source.manifest ? validatePluginManifest(
765
- source.manifestPath,
766
- source.manifest,
767
- pluginNameMap,
768
- providerDomainMap
769
- ) : await validatePluginDirectory(
770
- pluginDir,
771
- pluginNameMap,
772
- providerDomainMap
773
- );
774
- pluginResults.push({
775
- pluginDir,
776
- manifestPath: result.manifestPath,
777
- ...packageName ? { packageName } : void 0,
778
- ...result.manifest ? { manifest: result.manifest } : void 0,
779
- errors: result.errors,
780
- skillResults: []
781
- });
782
- errors.push(...result.errors);
783
- }
784
- const registeredConfigKeys = new Set(
785
- pluginResults.flatMap((result) => result.manifest?.configKeys ?? [])
786
- );
787
- const appSourceResult = await validateAppSourceFiles(
788
- resolvedRoot,
789
- registeredConfigKeys
790
- );
791
- warnings.push(...appSourceResult.warnings);
792
- errors.push(...appSourceResult.errors);
793
- for (const skillDir of appAndLocalPluginSkillDirs) {
794
- const result = await validateSkillDirectory(skillDir, duplicateSkillNames);
795
- skillResultsByDir.set(skillDir, result);
796
- warnings.push(...result.warnings);
797
- errors.push(...result.errors);
798
- }
799
- for (const skillDir of packagedSkillDirs) {
800
- const result = await validateSkillDirectory(
801
- skillDir,
802
- duplicatePackagedSkillNames
803
- );
804
- skillResultsByDir.set(skillDir, result);
805
- warnings.push(...result.warnings);
806
- errors.push(...result.errors);
807
- }
808
- for (const pluginResult of pluginResults) {
809
- pluginResult.skillResults = (pluginSkillDirs.get(pluginResult.pluginDir) ?? []).map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
810
- }
811
- const appSkillResults = appSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
812
- const packagedStandaloneSkillResultsByPackage = /* @__PURE__ */ new Map();
813
- for (const [packageName, packageSkillDirs] of packagedSkillDirsByPackage) {
814
- const packageSkillResults = packageSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
815
- if (packageSkillResults.length > 0) {
816
- packagedStandaloneSkillResultsByPackage.set(
817
- packageName,
818
- packageSkillResults
819
- );
820
- }
821
- }
822
- const appDir = path.resolve(resolvedRoot, "app");
823
- let appFileResult = {
824
- errors: [],
825
- warnings: []
826
- };
827
- const shouldValidateAppFiles = await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
828
- if (shouldValidateAppFiles) {
829
- appFileResult = await validateAppFiles(appDir);
830
- warnings.push(...appFileResult.warnings);
831
- errors.push(...appFileResult.errors);
832
- }
833
- io.info(
834
- `${color("Checking", ANSI.bold, ANSI.cyan)} ${color(
835
- formatDisplayPath(resolvedRoot, resolvedRoot),
836
- ANSI.dim
837
- )}`
838
- );
839
- if (shouldValidateAppFiles) {
840
- const appFileStatus = formatStatus(
841
- appFileResult.errors.length,
842
- appFileResult.warnings.length
843
- );
844
- io.info(formatHeading(appFileStatus, "app files"));
845
- }
846
- if (deploymentConfigResult.checked) {
847
- const deploymentConfigStatus = formatStatus(
848
- deploymentConfigResult.errors.length,
849
- deploymentConfigResult.warnings.length
850
- );
851
- io.info(formatHeading(deploymentConfigStatus, "deployment config"));
852
- }
853
- for (const pluginResult of pluginResults) {
854
- reportPluginResult(pluginResult, io);
855
- }
856
- for (const [
857
- packageName,
858
- packageSkillResults
859
- ] of packagedStandaloneSkillResultsByPackage) {
860
- reportSkillGroup(
861
- `packaged skills (${packageName})`,
862
- packageSkillResults,
863
- io
864
- );
865
- }
866
- if (appSkillResults.length > 0) {
867
- reportAppSkills(appSkillResults, io);
868
- }
869
- for (const warning of warnings) {
870
- io.warn(`${statusIcon("warn")} warning: ${warning}`);
871
- }
872
- if (errors.length > 0) {
873
- for (const error of errors) {
874
- io.error(`${statusIcon("error")} error: ${error}`);
875
- }
876
- throw new Error(
877
- `Validation failed (${errors.length} error${errors.length === 1 ? "" : "s"}, ${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`
878
- );
879
- }
880
- io.info(
881
- `${formatHeading("ok", `Validation passed (${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`)}`
882
- );
883
- }
884
- export {
885
- runCheck
886
- };
487
+ const resolvedRoot = path.resolve(rootDir);
488
+ if (!await pathIsDirectory(resolvedRoot)) throw new Error(`validation root does not exist or is not a directory: ${resolvedRoot}`);
489
+ const packages = declaredPackages(await readRootPackageJson(resolvedRoot));
490
+ const packageWarnings = await collectPackageWarnings(resolvedRoot, packages);
491
+ const packagedContent = await collectPackagedContent(resolvedRoot, packages);
492
+ const appPluginDirs = await collectPluginDirectories(resolvedRoot);
493
+ const packagedPluginSources = packagedContent.pluginSources;
494
+ const pluginSources = [...appPluginDirs.map((pluginDir) => ({
495
+ pluginDir,
496
+ manifestPath: path.join(pluginDir, "plugin.yaml")
497
+ })), ...packagedPluginSources];
498
+ const appSkillDirs = await collectSkillDirectories(contentRoot(resolvedRoot, "skills"));
499
+ const pluginSkillDirs = /* @__PURE__ */ new Map();
500
+ for (const { pluginDir } of pluginSources) pluginSkillDirs.set(pluginDir, await collectSkillDirectories(path.join(pluginDir, "skills")));
501
+ const packagedSkillDirsByPackage = /* @__PURE__ */ new Map();
502
+ for (const skillRoot of packagedContent.skillRoots) packagedSkillDirsByPackage.set(skillRoot.packageName, await collectSkillDirectories(skillRoot.root));
503
+ const packagedStandaloneSkillDirs = [...packagedSkillDirsByPackage.values()].flat();
504
+ const appAndLocalPluginSkillDirs = [...appSkillDirs, ...appPluginDirs.flatMap((pluginDir) => pluginSkillDirs.get(pluginDir) ?? [])].sort((left, right) => left.localeCompare(right));
505
+ const packagedSkillDirs = [...packagedPluginSources.flatMap(({ pluginDir }) => pluginSkillDirs.get(pluginDir) ?? []), ...packagedStandaloneSkillDirs].sort((left, right) => left.localeCompare(right));
506
+ const skillDirs = [...appAndLocalPluginSkillDirs, ...packagedSkillDirs].sort((left, right) => left.localeCompare(right));
507
+ const duplicateSkillNames = /* @__PURE__ */ new Map();
508
+ const duplicatePluginNames = /* @__PURE__ */ new Map();
509
+ const duplicateProviderDomains = /* @__PURE__ */ new Map();
510
+ const duplicatePackagedSkillNames = /* @__PURE__ */ new Map();
511
+ const duplicatePackagedPluginNames = /* @__PURE__ */ new Map();
512
+ const duplicatePackagedProviderDomains = /* @__PURE__ */ new Map();
513
+ const warnings = [];
514
+ const errors = [];
515
+ const pluginResults = [];
516
+ const skillResultsByDir = /* @__PURE__ */ new Map();
517
+ warnings.push(...packageWarnings);
518
+ const deploymentConfigResult = await validateDeploymentConfig(resolvedRoot, packages);
519
+ warnings.push(...deploymentConfigResult.warnings);
520
+ errors.push(...deploymentConfigResult.errors);
521
+ for (const source of pluginSources) {
522
+ const { pluginDir, packageName } = source;
523
+ const pluginNameMap = packageName ? duplicatePackagedPluginNames : duplicatePluginNames;
524
+ const providerDomainMap = packageName ? duplicatePackagedProviderDomains : duplicateProviderDomains;
525
+ const result = source.errors ? {
526
+ manifestPath: source.manifestPath,
527
+ ...source.manifest ? { manifest: source.manifest } : void 0,
528
+ errors: source.errors
529
+ } : source.manifest ? validatePluginManifest(source.manifestPath, source.manifest, pluginNameMap, providerDomainMap) : await validatePluginDirectory(pluginDir, pluginNameMap, providerDomainMap);
530
+ pluginResults.push({
531
+ pluginDir,
532
+ manifestPath: result.manifestPath,
533
+ ...packageName ? { packageName } : void 0,
534
+ ...result.manifest ? { manifest: result.manifest } : void 0,
535
+ errors: result.errors,
536
+ skillResults: []
537
+ });
538
+ errors.push(...result.errors);
539
+ }
540
+ const appSourceResult = await validateAppSourceFiles(resolvedRoot, new Set(pluginResults.flatMap((result) => result.manifest?.configKeys ?? [])));
541
+ warnings.push(...appSourceResult.warnings);
542
+ errors.push(...appSourceResult.errors);
543
+ for (const skillDir of appAndLocalPluginSkillDirs) {
544
+ const result = await validateSkillDirectory(skillDir, duplicateSkillNames);
545
+ skillResultsByDir.set(skillDir, result);
546
+ warnings.push(...result.warnings);
547
+ errors.push(...result.errors);
548
+ }
549
+ for (const skillDir of packagedSkillDirs) {
550
+ const result = await validateSkillDirectory(skillDir, duplicatePackagedSkillNames);
551
+ skillResultsByDir.set(skillDir, result);
552
+ warnings.push(...result.warnings);
553
+ errors.push(...result.errors);
554
+ }
555
+ for (const pluginResult of pluginResults) pluginResult.skillResults = (pluginSkillDirs.get(pluginResult.pluginDir) ?? []).map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
556
+ const appSkillResults = appSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
557
+ const packagedStandaloneSkillResultsByPackage = /* @__PURE__ */ new Map();
558
+ for (const [packageName, packageSkillDirs] of packagedSkillDirsByPackage) {
559
+ const packageSkillResults = packageSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
560
+ if (packageSkillResults.length > 0) packagedStandaloneSkillResultsByPackage.set(packageName, packageSkillResults);
561
+ }
562
+ const appDir = path.resolve(resolvedRoot, "app");
563
+ let appFileResult = {
564
+ errors: [],
565
+ warnings: []
566
+ };
567
+ const shouldValidateAppFiles = await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
568
+ if (shouldValidateAppFiles) {
569
+ appFileResult = await validateAppFiles(appDir);
570
+ warnings.push(...appFileResult.warnings);
571
+ errors.push(...appFileResult.errors);
572
+ }
573
+ io.info(`${color("Checking", ANSI.bold, ANSI.cyan)} ${color(formatDisplayPath(resolvedRoot, resolvedRoot), ANSI.dim)}`);
574
+ if (shouldValidateAppFiles) {
575
+ const appFileStatus = formatStatus(appFileResult.errors.length, appFileResult.warnings.length);
576
+ io.info(formatHeading(appFileStatus, "app files"));
577
+ }
578
+ if (deploymentConfigResult.checked) {
579
+ const deploymentConfigStatus = formatStatus(deploymentConfigResult.errors.length, deploymentConfigResult.warnings.length);
580
+ io.info(formatHeading(deploymentConfigStatus, "deployment config"));
581
+ }
582
+ for (const pluginResult of pluginResults) reportPluginResult(pluginResult, io);
583
+ for (const [packageName, packageSkillResults] of packagedStandaloneSkillResultsByPackage) reportSkillGroup(`packaged skills (${packageName})`, packageSkillResults, io);
584
+ if (appSkillResults.length > 0) reportAppSkills(appSkillResults, io);
585
+ for (const warning of warnings) io.warn(`${statusIcon("warn")} warning: ${warning}`);
586
+ if (errors.length > 0) {
587
+ for (const error of errors) io.error(`${statusIcon("error")} error: ${error}`);
588
+ throw new Error(`Validation failed (${errors.length} error${errors.length === 1 ? "" : "s"}, ${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`);
589
+ }
590
+ io.info(`${formatHeading("ok", `Validation passed (${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`)}`);
591
+ }
592
+ //#endregion
593
+ export { runCheck };