@jay-framework/jay-stack-cli 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -18,7 +18,6 @@ import path$1 from "node:path";
18
18
  import fs$1 from "node:fs/promises";
19
19
  import { createRequire } from "module";
20
20
  import { glob } from "glob";
21
- import { parse } from "node-html-parser";
22
21
  import fsSync from "node:fs";
23
22
  import { fileURLToPath } from "node:url";
24
23
  const DEFAULT_CONFIG = {
@@ -3022,6 +3021,769 @@ function checkParamsConsistency(paramsTypeNames, localInterfaces, contractImport
3022
3021
  }
3023
3022
  }
3024
3023
  }
3024
+ const REJECTED_ITEM_FIELDS = ["kind", "parameters", "component", "allowedScopes"];
3025
+ const HTML_SOFT_LIMIT_BYTES = 8 * 1024;
3026
+ const HTML_HARD_LIMIT_BYTES = 32 * 1024;
3027
+ const FOLDER_PATH_MAX_SEGMENTS = 32;
3028
+ const BLOCKED_TAGS = /<\s*(script|iframe|object|embed)\b[^>]*>[\s\S]*?<\/\s*\1\s*>|<\s*(script|iframe|object|embed)\b[^>]*\/?>/gi;
3029
+ const EVENT_HANDLER_ATTR = /\s+on[a-z]+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi;
3030
+ const JAVASCRIPT_URL = /\b(href|src|xlink:href)\s*=\s*("|')\s*javascript:/gi;
3031
+ function isRecord(value) {
3032
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3033
+ }
3034
+ function byteLengthUtf8(value) {
3035
+ return new TextEncoder().encode(value).length;
3036
+ }
3037
+ function detectUnsafeAddMenuHtmlFragment(html) {
3038
+ if (BLOCKED_TAGS.test(html)) {
3039
+ BLOCKED_TAGS.lastIndex = 0;
3040
+ return {
3041
+ code: "html-fragment-unsafe-markup",
3042
+ message: "html-fragment must not include script, iframe, object, or embed"
3043
+ };
3044
+ }
3045
+ BLOCKED_TAGS.lastIndex = 0;
3046
+ if (EVENT_HANDLER_ATTR.test(html)) {
3047
+ EVENT_HANDLER_ATTR.lastIndex = 0;
3048
+ return {
3049
+ code: "html-fragment-unsafe-markup",
3050
+ message: "html-fragment must not include inline event handler attributes"
3051
+ };
3052
+ }
3053
+ EVENT_HANDLER_ATTR.lastIndex = 0;
3054
+ if (JAVASCRIPT_URL.test(html)) {
3055
+ JAVASCRIPT_URL.lastIndex = 0;
3056
+ return {
3057
+ code: "html-fragment-unsafe-markup",
3058
+ message: "html-fragment must not include javascript: URLs"
3059
+ };
3060
+ }
3061
+ JAVASCRIPT_URL.lastIndex = 0;
3062
+ return null;
3063
+ }
3064
+ function sanitizeAddMenuHtmlFragment(html) {
3065
+ let result = html;
3066
+ result = result.replace(BLOCKED_TAGS, "");
3067
+ BLOCKED_TAGS.lastIndex = 0;
3068
+ result = result.replace(EVENT_HANDLER_ATTR, "");
3069
+ EVENT_HANDLER_ATTR.lastIndex = 0;
3070
+ result = result.replace(JAVASCRIPT_URL, "");
3071
+ JAVASCRIPT_URL.lastIndex = 0;
3072
+ return result.trim();
3073
+ }
3074
+ function catalogWarning(code, message, itemId, sourcePath) {
3075
+ return { code, message, ...itemId ? { itemId } : {}, ...sourcePath ? { sourcePath } : {} };
3076
+ }
3077
+ function requiredString(obj, field, itemPath, errors, code) {
3078
+ const value = obj[field];
3079
+ if (typeof value !== "string" || value.trim().length === 0) {
3080
+ errors.push({
3081
+ path: `${itemPath}.${field}`,
3082
+ message: "required non-empty string",
3083
+ ...code ? { code } : {}
3084
+ });
3085
+ return null;
3086
+ }
3087
+ return value.trim();
3088
+ }
3089
+ function optionalString(obj, field) {
3090
+ const value = obj[field];
3091
+ if (value === void 0)
3092
+ return void 0;
3093
+ if (typeof value !== "string")
3094
+ return void 0;
3095
+ const trimmed = value.trim();
3096
+ return trimmed.length > 0 ? trimmed : void 0;
3097
+ }
3098
+ function validateInteraction(raw, itemPath, errors) {
3099
+ if (raw === void 0)
3100
+ return void 0;
3101
+ if (!isRecord(raw)) {
3102
+ errors.push({
3103
+ path: itemPath,
3104
+ message: "interaction must be an object",
3105
+ code: "interaction-not-object"
3106
+ });
3107
+ return void 0;
3108
+ }
3109
+ const mode = raw.mode;
3110
+ if (mode !== "reference" && mode !== "stage-place") {
3111
+ errors.push({
3112
+ path: `${itemPath}.mode`,
3113
+ message: 'interaction.mode must be "reference" or "stage-place"',
3114
+ code: "interaction-invalid-mode"
3115
+ });
3116
+ return void 0;
3117
+ }
3118
+ return {
3119
+ mode,
3120
+ stagePromptTemplate: optionalString(raw, "stagePromptTemplate")
3121
+ };
3122
+ }
3123
+ function validatePresentation(raw, itemPath, errors) {
3124
+ if (raw === void 0)
3125
+ return void 0;
3126
+ if (!isRecord(raw)) {
3127
+ errors.push({
3128
+ path: itemPath,
3129
+ message: "presentation must be an object",
3130
+ code: "presentation-not-object"
3131
+ });
3132
+ return void 0;
3133
+ }
3134
+ const type = raw.type;
3135
+ if (type !== "image" && type !== "gif" && type !== "html-fragment") {
3136
+ errors.push({
3137
+ path: `${itemPath}.type`,
3138
+ message: 'presentation.type must be "image", "gif", or "html-fragment"',
3139
+ code: "presentation-unknown-type"
3140
+ });
3141
+ return void 0;
3142
+ }
3143
+ if (type === "image") {
3144
+ const src = requiredString(raw, "src", itemPath, errors, "presentation-missing-src");
3145
+ if (!src)
3146
+ return void 0;
3147
+ return { type: "image", src };
3148
+ }
3149
+ if (type === "gif") {
3150
+ const src = requiredString(raw, "src", itemPath, errors, "presentation-missing-src");
3151
+ if (!src)
3152
+ return void 0;
3153
+ return {
3154
+ type: "gif",
3155
+ src,
3156
+ poster: optionalString(raw, "poster")
3157
+ };
3158
+ }
3159
+ if ("src" in raw) {
3160
+ errors.push({
3161
+ path: `${itemPath}.src`,
3162
+ message: "html-fragment must use presentation.html, not presentation.src",
3163
+ code: "html-fragment-src-not-allowed"
3164
+ });
3165
+ }
3166
+ const htmlValue = raw.html;
3167
+ if (typeof htmlValue !== "string" || htmlValue.trim().length === 0) {
3168
+ errors.push({
3169
+ path: `${itemPath}.html`,
3170
+ message: "html-fragment requires non-empty presentation.html",
3171
+ code: "html-fragment-missing-html"
3172
+ });
3173
+ return void 0;
3174
+ }
3175
+ const unsafe = detectUnsafeAddMenuHtmlFragment(htmlValue);
3176
+ if (unsafe) {
3177
+ errors.push({
3178
+ path: `${itemPath}.html`,
3179
+ message: unsafe.message,
3180
+ code: unsafe.code
3181
+ });
3182
+ return void 0;
3183
+ }
3184
+ const sanitized = sanitizeAddMenuHtmlFragment(htmlValue);
3185
+ if (byteLengthUtf8(sanitized) > HTML_HARD_LIMIT_BYTES) {
3186
+ errors.push({
3187
+ path: `${itemPath}.html`,
3188
+ message: `html-fragment exceeds ${HTML_HARD_LIMIT_BYTES} bytes after sanitize`,
3189
+ code: "html-fragment-oversize-hard"
3190
+ });
3191
+ return void 0;
3192
+ }
3193
+ return { type: "html-fragment", html: htmlValue };
3194
+ }
3195
+ const BROWSE_SIZES = /* @__PURE__ */ new Set(["large", "medium", "small"]);
3196
+ function validateBrowse(raw, itemPath, errors) {
3197
+ if (raw === void 0)
3198
+ return void 0;
3199
+ if (!isRecord(raw)) {
3200
+ errors.push({
3201
+ path: itemPath,
3202
+ message: "browse must be an object",
3203
+ code: "browse-not-object"
3204
+ });
3205
+ return void 0;
3206
+ }
3207
+ const sizeRaw = raw.size;
3208
+ if (sizeRaw === void 0) {
3209
+ return {};
3210
+ }
3211
+ if (typeof sizeRaw !== "string" || !BROWSE_SIZES.has(sizeRaw)) {
3212
+ errors.push({
3213
+ path: `${itemPath}.size`,
3214
+ message: 'browse.size must be "large", "medium", or "small"',
3215
+ code: "browse-unknown-size"
3216
+ });
3217
+ return void 0;
3218
+ }
3219
+ return { size: sizeRaw };
3220
+ }
3221
+ function validateFolderPath(raw, itemPath, errors) {
3222
+ if (raw === void 0)
3223
+ return void 0;
3224
+ if (!Array.isArray(raw)) {
3225
+ errors.push({
3226
+ path: `${itemPath}.folderPath`,
3227
+ message: "folderPath must be an array of strings",
3228
+ code: "folder-path-not-array"
3229
+ });
3230
+ return void 0;
3231
+ }
3232
+ const segments = [];
3233
+ for (let index = 0; index < raw.length; index++) {
3234
+ const value = raw[index];
3235
+ if (typeof value !== "string") {
3236
+ errors.push({
3237
+ path: `${itemPath}.folderPath[${index}]`,
3238
+ message: "folderPath segments must be strings",
3239
+ code: "folder-path-segment-type"
3240
+ });
3241
+ return void 0;
3242
+ }
3243
+ const trimmed = value.trim();
3244
+ if (trimmed.length === 0) {
3245
+ errors.push({
3246
+ path: `${itemPath}.folderPath[${index}]`,
3247
+ message: "folderPath segments must be non-empty",
3248
+ code: "folder-path-empty-segment"
3249
+ });
3250
+ return void 0;
3251
+ }
3252
+ if (trimmed === ".." || trimmed.includes("/") || trimmed.includes("\\")) {
3253
+ errors.push({
3254
+ path: `${itemPath}.folderPath[${index}]`,
3255
+ message: 'folderPath segments must not contain ".." or path separators',
3256
+ code: "folder-path-invalid-segment"
3257
+ });
3258
+ return void 0;
3259
+ }
3260
+ if (segments.at(-1) === trimmed) {
3261
+ errors.push({
3262
+ path: `${itemPath}.folderPath[${index}]`,
3263
+ message: "folderPath must not contain duplicate adjacent segments",
3264
+ code: "folder-path-adjacent-duplicate"
3265
+ });
3266
+ return void 0;
3267
+ }
3268
+ segments.push(trimmed);
3269
+ }
3270
+ if (segments.length > FOLDER_PATH_MAX_SEGMENTS) {
3271
+ errors.push({
3272
+ path: `${itemPath}.folderPath`,
3273
+ message: `folderPath exceeds ${FOLDER_PATH_MAX_SEGMENTS} segments`,
3274
+ code: "folder-path-too-deep"
3275
+ });
3276
+ return void 0;
3277
+ }
3278
+ return segments.length > 0 ? segments : void 0;
3279
+ }
3280
+ function validateAddMenuItem(raw, itemPath) {
3281
+ const errors = [];
3282
+ if (!isRecord(raw)) {
3283
+ return {
3284
+ item: null,
3285
+ errors: [
3286
+ { path: itemPath, message: "item must be an object", code: "item-not-object" }
3287
+ ]
3288
+ };
3289
+ }
3290
+ for (const field of REJECTED_ITEM_FIELDS) {
3291
+ if (field in raw) {
3292
+ errors.push({
3293
+ path: `${itemPath}.${field}`,
3294
+ message: `field "${field}" is not allowed in Add Menu catalog items`,
3295
+ code: `rejected-field-${field}`
3296
+ });
3297
+ }
3298
+ }
3299
+ const id = requiredString(raw, "id", itemPath, errors, "missing-id");
3300
+ const title = requiredString(raw, "title", itemPath, errors, "missing-title");
3301
+ const category = requiredString(raw, "category", itemPath, errors, "missing-category");
3302
+ const prompt = requiredString(raw, "prompt", itemPath, errors, "missing-prompt");
3303
+ if (errors.length > 0 || !id || !title || !category || !prompt) {
3304
+ return { item: null, errors };
3305
+ }
3306
+ const interaction = validateInteraction(raw.interaction, `${itemPath}.interaction`, errors);
3307
+ const presentation = validatePresentation(raw.presentation, `${itemPath}.presentation`, errors);
3308
+ const browse = validateBrowse(raw.browse, `${itemPath}.browse`, errors);
3309
+ const folderPath = validateFolderPath(raw.folderPath, itemPath, errors);
3310
+ if (errors.length > 0) {
3311
+ return { item: null, errors };
3312
+ }
3313
+ return {
3314
+ item: {
3315
+ id,
3316
+ title,
3317
+ category,
3318
+ prompt,
3319
+ pluginName: optionalString(raw, "pluginName"),
3320
+ packageName: optionalString(raw, "packageName"),
3321
+ subCategory: optionalString(raw, "subCategory"),
3322
+ ...folderPath ? { folderPath } : {},
3323
+ thumbnail: optionalString(raw, "thumbnail"),
3324
+ ...presentation ? { presentation } : {},
3325
+ ...browse ? { browse } : {},
3326
+ ...interaction ? { interaction } : {}
3327
+ },
3328
+ errors
3329
+ };
3330
+ }
3331
+ function validateAddMenuCatalogFile(raw, sourcePath) {
3332
+ const errors = [];
3333
+ if (!isRecord(raw)) {
3334
+ return {
3335
+ file: null,
3336
+ errors: [
3337
+ {
3338
+ path: sourcePath,
3339
+ message: "catalog file must be an object",
3340
+ code: "catalog-not-object"
3341
+ }
3342
+ ]
3343
+ };
3344
+ }
3345
+ if (!Array.isArray(raw.items)) {
3346
+ return {
3347
+ file: null,
3348
+ errors: [
3349
+ {
3350
+ path: `${sourcePath}.items`,
3351
+ message: "items must be an array",
3352
+ code: "catalog-items-not-array"
3353
+ }
3354
+ ]
3355
+ };
3356
+ }
3357
+ const items = [];
3358
+ raw.items.forEach((entry, index) => {
3359
+ const result = validateAddMenuItem(entry, `${sourcePath}.items[${index}]`);
3360
+ errors.push(...result.errors);
3361
+ if (result.item)
3362
+ items.push(result.item);
3363
+ });
3364
+ if (items.length === 0 && errors.length > 0) {
3365
+ return { file: null, errors };
3366
+ }
3367
+ return { file: { items }, errors };
3368
+ }
3369
+ function normalizeAddMenuPresentation(item) {
3370
+ if (item.presentation)
3371
+ return item.presentation;
3372
+ const thumbnail = item.thumbnail?.trim();
3373
+ if (!thumbnail)
3374
+ return void 0;
3375
+ if (/\.gif$/i.test(thumbnail)) {
3376
+ return { type: "gif", src: thumbnail };
3377
+ }
3378
+ return { type: "image", src: thumbnail };
3379
+ }
3380
+ function normalizeAddMenuBrowseSize(item) {
3381
+ return item.browse?.size ?? "medium";
3382
+ }
3383
+ function hasSingleRootDiv(html) {
3384
+ const trimmed = html.trim();
3385
+ if (!trimmed.startsWith("<div"))
3386
+ return false;
3387
+ const openMatch = trimmed.match(/^<div\b[^>]*>/i);
3388
+ if (!openMatch)
3389
+ return false;
3390
+ const afterOpen = trimmed.slice(openMatch[0].length);
3391
+ const closeIdx = afterOpen.lastIndexOf("</div>");
3392
+ if (closeIdx < 0)
3393
+ return false;
3394
+ const tail = afterOpen.slice(closeIdx + "</div>".length).trim();
3395
+ return tail.length === 0;
3396
+ }
3397
+ function hasAtScopeStyle(html) {
3398
+ return /<style\b[^>]*>[\s\S]*@scope\b/i.test(html);
3399
+ }
3400
+ function lintHtmlFragment(item, sourcePath) {
3401
+ const errors = [];
3402
+ const warnings = [];
3403
+ const presentation = item.presentation;
3404
+ if (!presentation || presentation.type !== "html-fragment") {
3405
+ return { errors, warnings };
3406
+ }
3407
+ const rawHtml = presentation.html?.trim() ?? "";
3408
+ if (!rawHtml) {
3409
+ errors.push(
3410
+ catalogWarning(
3411
+ "html-fragment-missing-html",
3412
+ "html-fragment requires non-empty presentation.html",
3413
+ item.id,
3414
+ sourcePath
3415
+ )
3416
+ );
3417
+ return { errors, warnings };
3418
+ }
3419
+ const unsafe = detectUnsafeAddMenuHtmlFragment(rawHtml);
3420
+ if (unsafe) {
3421
+ errors.push(catalogWarning(unsafe.code, unsafe.message, item.id, sourcePath));
3422
+ return { errors, warnings };
3423
+ }
3424
+ const sanitized = sanitizeAddMenuHtmlFragment(rawHtml);
3425
+ const bytes = byteLengthUtf8(sanitized);
3426
+ if (bytes > HTML_HARD_LIMIT_BYTES) {
3427
+ errors.push(
3428
+ catalogWarning(
3429
+ "html-fragment-oversize-hard",
3430
+ `html-fragment exceeds ${HTML_HARD_LIMIT_BYTES} bytes after sanitize (${bytes})`,
3431
+ item.id,
3432
+ sourcePath
3433
+ )
3434
+ );
3435
+ } else if (bytes > HTML_SOFT_LIMIT_BYTES) {
3436
+ warnings.push(
3437
+ catalogWarning(
3438
+ "html-fragment-oversize-soft",
3439
+ `html-fragment exceeds ${HTML_SOFT_LIMIT_BYTES} bytes after sanitize (${bytes}) — consider trimming`,
3440
+ item.id,
3441
+ sourcePath
3442
+ )
3443
+ );
3444
+ }
3445
+ if (!hasSingleRootDiv(sanitized)) {
3446
+ warnings.push(
3447
+ catalogWarning(
3448
+ "html-fragment-missing-root-div",
3449
+ "html-fragment should use a single root <div>",
3450
+ item.id,
3451
+ sourcePath
3452
+ )
3453
+ );
3454
+ }
3455
+ if (!hasAtScopeStyle(sanitized)) {
3456
+ warnings.push(
3457
+ catalogWarning(
3458
+ "html-fragment-missing-at-scope",
3459
+ "html-fragment should include <style> with @scope for preview isolation",
3460
+ item.id,
3461
+ sourcePath
3462
+ )
3463
+ );
3464
+ }
3465
+ return { errors, warnings };
3466
+ }
3467
+ function lintGifPoster(item, sourcePath) {
3468
+ const presentation = normalizeAddMenuPresentation(item);
3469
+ if (presentation?.type !== "gif" || presentation.poster?.trim())
3470
+ return [];
3471
+ return [
3472
+ catalogWarning(
3473
+ "gif-missing-poster",
3474
+ `gif item "${item.id}" has no poster — add poster for prefers-reduced-motion accessibility`,
3475
+ item.id,
3476
+ sourcePath
3477
+ )
3478
+ ];
3479
+ }
3480
+ function lintBrowseLargeWithoutPresentation(item, sourcePath) {
3481
+ if (normalizeAddMenuBrowseSize(item) !== "large")
3482
+ return [];
3483
+ if (normalizeAddMenuPresentation(item))
3484
+ return [];
3485
+ return [
3486
+ catalogWarning(
3487
+ "browse-large-without-presentation",
3488
+ `large browse item "${item.id}" has no presentation or thumbnail — add a preview`,
3489
+ item.id,
3490
+ sourcePath
3491
+ )
3492
+ ];
3493
+ }
3494
+ function lintAddMenuCatalog(items, sourcePath) {
3495
+ const errors = [];
3496
+ const warnings = [];
3497
+ for (const item of items) {
3498
+ warnings.push(...lintGifPoster(item, sourcePath));
3499
+ warnings.push(...lintBrowseLargeWithoutPresentation(item, sourcePath));
3500
+ const htmlLint = lintHtmlFragment(item, sourcePath);
3501
+ errors.push(...htmlLint.errors);
3502
+ warnings.push(...htmlLint.warnings);
3503
+ }
3504
+ return { errors, warnings };
3505
+ }
3506
+ const ADD_MENU_VALIDATION_SUGGESTIONS = {
3507
+ "gif-missing-poster": "Add presentation.poster with a static image for prefers-reduced-motion accessibility",
3508
+ "html-fragment-missing-root-div": 'Wrap preview markup in a single root <div class="am-preview"> — see agent-kit/plugin/aiditor-add-menu.md',
3509
+ "html-fragment-missing-at-scope": "Add <style> with @scope (.am-preview) inside the root div — see agent-kit/plugin/aiditor-add-menu.md",
3510
+ "html-fragment-missing-html": "Set presentation.html with inline markup — external preview files are not supported",
3511
+ "html-fragment-src-not-allowed": "Remove presentation.src; use presentation.html for html-fragment previews",
3512
+ "html-fragment-unsafe-markup": "Remove scripts, event handlers, and javascript: URLs from presentation.html",
3513
+ "html-fragment-oversize-soft": "Trim inline html or use a gif preview for rich motion",
3514
+ "html-fragment-oversize-hard": "Reduce presentation.html below 32 KB after sanitize",
3515
+ "browse-large-without-presentation": "Add presentation or thumbnail — large browse tiles need a visual preview",
3516
+ "browse-unknown-size": "Set browse.size to large, medium, or small",
3517
+ "presentation-unknown-type": "Set presentation.type to image, gif, or html-fragment",
3518
+ "interaction-invalid-mode": "Set interaction.mode to reference (default) or stage-place",
3519
+ "folder-path-not-array": "folderPath must be a yaml array of folder name strings",
3520
+ "folder-path-segment-type": "Each folderPath entry must be a non-empty string",
3521
+ "folder-path-empty-segment": "Remove empty folderPath segments",
3522
+ "folder-path-invalid-segment": 'Use separate array elements per folder level — no "/" or "\\" in segment names',
3523
+ "folder-path-adjacent-duplicate": "Remove duplicate consecutive folderPath segments",
3524
+ "folder-path-too-deep": `Shorten folderPath to at most ${FOLDER_PATH_MAX_SEGMENTS} segments`,
3525
+ "rejected-field-kind": "Remove kind — express item intent in prompt text",
3526
+ "rejected-field-parameters": "Remove parameters — materialize values into prompt",
3527
+ "rejected-field-component": "Remove component — use prompt plus contract paths in agent-kit",
3528
+ "rejected-field-allowedScopes": "Remove allowedScopes — attachment scope is chosen in the AIditor UI",
3529
+ "add-menu-missing-agentkit-handler": "Declare agentkit in plugin.yaml pointing at an agent-kit handler export; run jay-stack agent-kit (project yarn agent-kit) to materialize catalogs — not jay-stack setup",
3530
+ "add-menu-legacy-setup-handler": "Move add-menu catalog writes from the setup handler to agentkit. Setup is for config/credentials only; run jay-stack agent-kit to generate agent-kit/aiditor/add-menu/*.yaml",
3531
+ "deprecated-nested-setup-keys": "Replace nested setup.handler / setup.references with flat keys: setup: <handler> and agentkit: <handler>"
3532
+ };
3533
+ const ADD_MENU_CATALOG_REL_PATHS = [
3534
+ "agent-kit/aiditor/add-menu.template.yaml",
3535
+ "agent-kit/aiditor/add-menu.yaml"
3536
+ ];
3537
+ const CONTRIBUTOR_GUIDE = "agent-kit/plugin/aiditor-add-menu.md";
3538
+ const ADD_MENU_WRITE_MARKERS = [
3539
+ "agent-kit/aiditor/add-menu",
3540
+ "aiditor-add-menu-thumbnails",
3541
+ "writeAddMenuCatalog",
3542
+ "copyAiditorAddMenuThumbnails"
3543
+ ];
3544
+ function isRelativeHandlerRef(value) {
3545
+ return value.startsWith("./") || value.startsWith("../");
3546
+ }
3547
+ function resolveModulePath$1(basePath) {
3548
+ for (const ext of ["", ".ts", ".js", "/index.ts", "/index.js"]) {
3549
+ const candidate = basePath + ext;
3550
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
3551
+ return candidate;
3552
+ }
3553
+ }
3554
+ return void 0;
3555
+ }
3556
+ function collectTypeScriptFiles(dir, depth = 0) {
3557
+ if (depth > 4)
3558
+ return [];
3559
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
3560
+ const files = [];
3561
+ for (const entry of entries) {
3562
+ const fullPath = path.join(dir, entry.name);
3563
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "test") {
3564
+ files.push(...collectTypeScriptFiles(fullPath, depth + 1));
3565
+ } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js"))) {
3566
+ files.push(fullPath);
3567
+ }
3568
+ }
3569
+ return files;
3570
+ }
3571
+ function resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage) {
3572
+ if (isRelativeHandlerRef(handlerRef)) {
3573
+ return resolveModulePath$1(path.join(pluginPath, handlerRef)) ?? null;
3574
+ }
3575
+ const searchRoots = isNpmPackage ? [path.join(pluginPath, "lib"), path.join(pluginPath, "dist")] : [pluginPath];
3576
+ for (const root of searchRoots) {
3577
+ if (!fs.existsSync(root))
3578
+ continue;
3579
+ for (const file of collectTypeScriptFiles(root)) {
3580
+ const content = fs.readFileSync(file, "utf-8");
3581
+ const definesHandler = new RegExp(
3582
+ `export\\s+(?:async\\s+)?function\\s+${handlerRef}\\b`
3583
+ ).test(content);
3584
+ const definesDefaultNamed = new RegExp(
3585
+ `export\\s+default\\s+async\\s+function\\s+${handlerRef}\\b`
3586
+ ).test(content);
3587
+ if (definesHandler || definesDefaultNamed) {
3588
+ return file;
3589
+ }
3590
+ const reExportMatch = content.match(
3591
+ new RegExp(
3592
+ `export\\s*\\{[^}]*\\b${handlerRef}\\b[^}]*\\}\\s*from\\s*['"]([^'"]+)['"]`
3593
+ )
3594
+ );
3595
+ if (reExportMatch) {
3596
+ const importSpec = reExportMatch[1].replace(/\.js$/, "");
3597
+ const resolved = resolveModulePath$1(path.resolve(path.dirname(file), importSpec));
3598
+ if (resolved)
3599
+ return resolved;
3600
+ }
3601
+ }
3602
+ }
3603
+ return null;
3604
+ }
3605
+ function extractBalancedBlock(source, openBraceIndex) {
3606
+ let depth = 0;
3607
+ for (let index = openBraceIndex; index < source.length; index++) {
3608
+ const char = source[index];
3609
+ if (char === "{")
3610
+ depth++;
3611
+ else if (char === "}") {
3612
+ depth--;
3613
+ if (depth === 0) {
3614
+ return source.slice(openBraceIndex, index + 1);
3615
+ }
3616
+ }
3617
+ }
3618
+ return null;
3619
+ }
3620
+ function findFunctionBodyOpenBrace(source, searchFrom) {
3621
+ const arrowMatch = /=>\s*\{/.exec(source.slice(searchFrom));
3622
+ const parenMatch = /\)\s*(?::[^{]+)?\{/.exec(source.slice(searchFrom));
3623
+ const candidates = [];
3624
+ if (arrowMatch?.index !== void 0) {
3625
+ candidates.push(searchFrom + arrowMatch.index + arrowMatch[0].length - 1);
3626
+ }
3627
+ if (parenMatch?.index !== void 0) {
3628
+ candidates.push(searchFrom + parenMatch.index + parenMatch[0].length - 1);
3629
+ }
3630
+ if (candidates.length === 0)
3631
+ return -1;
3632
+ return Math.min(...candidates);
3633
+ }
3634
+ function extractFunctionBody(source, functionName) {
3635
+ const patterns = [
3636
+ new RegExp(`export\\s+async\\s+function\\s+${functionName}\\b`),
3637
+ new RegExp(`export\\s+function\\s+${functionName}\\b`),
3638
+ new RegExp(`export\\s+default\\s+async\\s+function\\s+${functionName}\\b`),
3639
+ new RegExp(
3640
+ `export\\s+const\\s+${functionName}\\s*=\\s*async\\s*\\([^)]*\\)\\s*(?::[^{]+)?=>\\s*\\{`
3641
+ ),
3642
+ new RegExp(
3643
+ `export\\s+const\\s+${functionName}\\s*=\\s*\\([^)]*\\)\\s*(?::[^{]+)?=>\\s*\\{`
3644
+ )
3645
+ ];
3646
+ for (const pattern of patterns) {
3647
+ const match = pattern.exec(source);
3648
+ if (!match)
3649
+ continue;
3650
+ const braceIndex = findFunctionBodyOpenBrace(source, match.index);
3651
+ if (braceIndex === -1)
3652
+ continue;
3653
+ return extractBalancedBlock(source, braceIndex);
3654
+ }
3655
+ return null;
3656
+ }
3657
+ function extractDefaultExportFunctionBody(source) {
3658
+ const patterns = [
3659
+ /export\s+default\s+async\s+function\s+\w+\b/,
3660
+ /export\s+default\s+async\s+function\s*\(/,
3661
+ /export\s+default\s+async\s*\([^)]*\)\s*(?::[^{]+)?=>\s*\{/
3662
+ ];
3663
+ for (const pattern of patterns) {
3664
+ const match = pattern.exec(source);
3665
+ if (!match)
3666
+ continue;
3667
+ const braceIndex = findFunctionBodyOpenBrace(source, match.index);
3668
+ if (braceIndex === -1)
3669
+ continue;
3670
+ return extractBalancedBlock(source, braceIndex);
3671
+ }
3672
+ return null;
3673
+ }
3674
+ function handlerBodyWritesAddMenuCatalog(body) {
3675
+ return ADD_MENU_WRITE_MARKERS.some((marker) => body.includes(marker));
3676
+ }
3677
+ function resolveSetupHandlerFunctionBody(pluginPath, handlerRef, isNpmPackage) {
3678
+ const sourceFile = resolveHandlerSourceFile(pluginPath, handlerRef, isNpmPackage);
3679
+ if (!sourceFile)
3680
+ return null;
3681
+ const source = fs.readFileSync(sourceFile, "utf-8");
3682
+ if (isRelativeHandlerRef(handlerRef)) {
3683
+ return extractDefaultExportFunctionBody(source) ?? extractFunctionBody(source, "setup") ?? extractFunctionBody(source, handlerRef);
3684
+ }
3685
+ return extractFunctionBody(source, handlerRef);
3686
+ }
3687
+ function suggestionForCode(code) {
3688
+ if (!code)
3689
+ return `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
3690
+ return ADD_MENU_VALIDATION_SUGGESTIONS[code] ?? `See ${CONTRIBUTOR_GUIDE} for schema and validation rules`;
3691
+ }
3692
+ function mapSchemaError(error, catalogPath) {
3693
+ const code = error.code ?? "catalog-validation-error";
3694
+ return {
3695
+ type: "add-menu-catalog",
3696
+ code,
3697
+ message: `[${code}] ${error.message}`,
3698
+ location: error.path || catalogPath,
3699
+ suggestion: suggestionForCode(code)
3700
+ };
3701
+ }
3702
+ function mapLintFinding(finding, catalogPath, severity) {
3703
+ return {
3704
+ type: "add-menu-catalog",
3705
+ code: finding.code,
3706
+ message: `[${finding.code}] ${finding.message}`,
3707
+ location: finding.sourcePath ?? catalogPath,
3708
+ itemId: finding.itemId,
3709
+ suggestion: suggestionForCode(finding.code),
3710
+ ...severity === "warning" ? {} : {}
3711
+ };
3712
+ }
3713
+ function pluginShipsAddMenuCatalog(context) {
3714
+ return ADD_MENU_CATALOG_REL_PATHS.some(
3715
+ (relPath) => fs.existsSync(path.join(context.pluginPath, relPath))
3716
+ );
3717
+ }
3718
+ function validateAddMenuAgentKitHandler(context, result) {
3719
+ if (!pluginShipsAddMenuCatalog(context))
3720
+ return;
3721
+ const agentKitHandler = context.manifest.agentkit;
3722
+ if (!agentKitHandler) {
3723
+ result.warnings.push({
3724
+ type: "add-menu-catalog",
3725
+ code: "add-menu-missing-agentkit-handler",
3726
+ message: "[add-menu-missing-agentkit-handler] Plugin ships add-menu catalog yaml but plugin.yaml has no agentkit handler — catalogs must be generated during jay-stack agent-kit (yarn agent-kit), not jay-stack setup",
3727
+ location: "plugin.yaml",
3728
+ suggestion: suggestionForCode("add-menu-missing-agentkit-handler")
3729
+ });
3730
+ }
3731
+ const setupHandler = typeof context.manifest.setup === "string" ? context.manifest.setup : void 0;
3732
+ if (!setupHandler)
3733
+ return;
3734
+ const setupBody = resolveSetupHandlerFunctionBody(
3735
+ context.pluginPath,
3736
+ setupHandler,
3737
+ context.isNpmPackage
3738
+ );
3739
+ if (!setupBody || !handlerBodyWritesAddMenuCatalog(setupBody))
3740
+ return;
3741
+ result.warnings.push({
3742
+ type: "add-menu-catalog",
3743
+ code: "add-menu-legacy-setup-handler",
3744
+ message: "[add-menu-legacy-setup-handler] setup handler writes add-menu catalogs — move catalog materialization to agentkit and run jay-stack agent-kit (yarn agent-kit); keep setup for config and credentials only",
3745
+ location: `plugin.yaml setup (${setupHandler})`,
3746
+ suggestion: suggestionForCode("add-menu-legacy-setup-handler")
3747
+ });
3748
+ }
3749
+ async function validateAddMenuCatalogFileAtPath(catalogPath, relPath, result) {
3750
+ let parsed;
3751
+ try {
3752
+ const content = await fs.promises.readFile(catalogPath, "utf-8");
3753
+ parsed = YAML.parse(content);
3754
+ } catch (error) {
3755
+ const message = error instanceof Error ? error.message : String(error);
3756
+ result.errors.push({
3757
+ type: "add-menu-catalog",
3758
+ code: "catalog-yaml-parse-error",
3759
+ message: `Invalid add-menu catalog YAML: ${message}`,
3760
+ location: relPath,
3761
+ suggestion: `Check YAML syntax in the add-menu catalog file. See ${CONTRIBUTOR_GUIDE}`
3762
+ });
3763
+ return;
3764
+ }
3765
+ const validated = validateAddMenuCatalogFile(parsed, relPath);
3766
+ result.errors.push(...validated.errors.map((error) => mapSchemaError(error, relPath)));
3767
+ if (!validated.file?.items.length) {
3768
+ return;
3769
+ }
3770
+ const linted = lintAddMenuCatalog(validated.file.items, relPath);
3771
+ result.errors.push(
3772
+ ...linted.errors.map((finding) => mapLintFinding(finding, relPath, "error"))
3773
+ );
3774
+ result.warnings.push(
3775
+ ...linted.warnings.map((finding) => mapLintFinding(finding, relPath, "warning"))
3776
+ );
3777
+ }
3778
+ async function validateAddMenuCatalog(context, result) {
3779
+ validateAddMenuAgentKitHandler(context, result);
3780
+ for (const relPath of ADD_MENU_CATALOG_REL_PATHS) {
3781
+ const catalogPath = path.join(context.pluginPath, relPath);
3782
+ if (!fs.existsSync(catalogPath))
3783
+ continue;
3784
+ await validateAddMenuCatalogFileAtPath(catalogPath, relPath, result);
3785
+ }
3786
+ }
3025
3787
  async function validatePlugin(options = {}) {
3026
3788
  const pluginPath = options.pluginPath || process.cwd();
3027
3789
  if (options.local) {
@@ -3088,6 +3850,7 @@ async function validatePluginPackage(pluginPath, options) {
3088
3850
  if (pluginManifest.dynamic_contracts) {
3089
3851
  await validateDynamicContracts(context, result);
3090
3852
  }
3853
+ await validateAddMenuCatalog(context, result);
3091
3854
  result.valid = result.errors.length === 0;
3092
3855
  return result;
3093
3856
  }
@@ -3437,25 +4200,32 @@ async function validateSchema(context, result) {
3437
4200
  }
3438
4201
  }
3439
4202
  if (manifest.setup) {
3440
- if (manifest.setup.handler) {
4203
+ if (typeof manifest.setup !== "string") {
4204
+ result.errors.push({
4205
+ type: "schema",
4206
+ message: "Deprecated nested setup keys (setup.handler / setup.references) — use flat setup: and agentkit: in plugin.yaml",
4207
+ location: "plugin.yaml setup",
4208
+ suggestion: "Replace setup.handler with top-level setup: and setup.references with top-level agentkit:"
4209
+ });
4210
+ } else {
3441
4211
  validateHandlerRef(
3442
- manifest.setup.handler,
4212
+ manifest.setup,
3443
4213
  "Setup handler",
3444
- "plugin.yaml setup.handler",
3445
- context,
3446
- result
3447
- );
3448
- }
3449
- if (manifest.setup.references) {
3450
- validateHandlerRef(
3451
- manifest.setup.references,
3452
- "References handler",
3453
- "plugin.yaml setup.references",
4214
+ "plugin.yaml setup",
3454
4215
  context,
3455
4216
  result
3456
4217
  );
3457
4218
  }
3458
4219
  }
4220
+ if (manifest.agentkit) {
4221
+ validateHandlerRef(
4222
+ manifest.agentkit,
4223
+ "Agent-kit handler",
4224
+ "plugin.yaml agentkit",
4225
+ context,
4226
+ result
4227
+ );
4228
+ }
3459
4229
  }
3460
4230
  function checkExportExists(exportName, context) {
3461
4231
  const packageJsonPath = path.join(context.pluginPath, "package.json");
@@ -3842,6 +4612,18 @@ async function validatePackageJson(context, result) {
3842
4612
  suggestion: 'Add "./plugin.yaml": "./plugin.yaml" to exports field'
3843
4613
  });
3844
4614
  }
4615
+ const agentKitDir = path.join(context.pluginPath, "agent-kit");
4616
+ if (fs.existsSync(agentKitDir) && fs.statSync(agentKitDir).isDirectory()) {
4617
+ const filesArray = packageJson.files;
4618
+ if (!filesArray || !filesArray.includes("agent-kit")) {
4619
+ result.warnings.push({
4620
+ type: "export-mismatch",
4621
+ message: 'agent-kit directory exists but is not listed in package.json "files"',
4622
+ location: packageJsonPath,
4623
+ suggestion: 'Add "agent-kit" to the "files" array so agent-kit files are shipped with the package'
4624
+ });
4625
+ }
4626
+ }
3845
4627
  } catch (error) {
3846
4628
  result.errors.push({
3847
4629
  type: "schema",
@@ -4233,38 +5015,18 @@ function extractRouteParams(filePath, pagesBase) {
4233
5015
  }
4234
5016
  return params;
4235
5017
  }
4236
- function dedentYaml(text) {
4237
- const lines = text.split("\n").filter((l) => l.trim().length > 0);
4238
- if (lines.length === 0)
4239
- return "";
4240
- const minIndent = Math.min(...lines.map((l) => l.match(/^\s*/)?.[0].length ?? 0));
4241
- return lines.map((l) => l.slice(minIndent)).join("\n");
4242
- }
4243
- function extractJayParams(content) {
4244
- const root = parse(content, {
4245
- comment: true,
4246
- blockTextElements: { script: true, style: true }
4247
- });
4248
- const head = root.querySelector("head");
4249
- if (!head)
4250
- return /* @__PURE__ */ new Set();
4251
- const paramScripts = head.querySelectorAll('script[type="application/jay-params"]');
4252
- if (paramScripts.length !== 1)
4253
- return /* @__PURE__ */ new Set();
4254
- const body = dedentYaml(paramScripts[0].textContent ?? "");
4255
- if (!body)
4256
- return /* @__PURE__ */ new Set();
4257
- try {
4258
- const parsed = YAML.parse(body);
4259
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
4260
- return new Set(Object.keys(parsed));
5018
+ function extractHeadlessPropsParamNames(parsedFile) {
5019
+ const names = /* @__PURE__ */ new Set();
5020
+ for (const imp of parsedFile.headlessImports) {
5021
+ if (imp.headlessProps) {
5022
+ for (const key of Object.keys(imp.headlessProps)) {
5023
+ names.add(key);
5024
+ }
4261
5025
  }
4262
- return /* @__PURE__ */ new Set();
4263
- } catch {
4264
- return /* @__PURE__ */ new Set();
4265
5026
  }
5027
+ return names;
4266
5028
  }
4267
- function checkRouteParams(parsedFile, filePath, pagesBase, jayHtmlContent) {
5029
+ function checkRouteParams(parsedFile, filePath, pagesBase) {
4268
5030
  const requiredParams = /* @__PURE__ */ new Set();
4269
5031
  function collectParams(params) {
4270
5032
  for (const p of params) {
@@ -4284,13 +5046,13 @@ function checkRouteParams(parsedFile, filePath, pagesBase, jayHtmlContent) {
4284
5046
  if (requiredParams.size === 0)
4285
5047
  return [];
4286
5048
  const routeParams = extractRouteParams(filePath, pagesBase);
4287
- const jayParams = extractJayParams(jayHtmlContent);
4288
- const availableParams = /* @__PURE__ */ new Set([...routeParams, ...jayParams]);
5049
+ const headlessProps = extractHeadlessPropsParamNames(parsedFile);
5050
+ const availableParams = /* @__PURE__ */ new Set([...routeParams, ...headlessProps]);
4289
5051
  const warnings = [];
4290
5052
  for (const param of requiredParams) {
4291
5053
  if (!availableParams.has(param)) {
4292
5054
  warnings.push(
4293
- `Contract requires param "${param}" but the route does not provide it. Add a dynamic segment [${param}] to the route path or declare it in <script type="application/jay-params">.`
5055
+ `Contract requires param "${param}" but the route does not provide it. Add a dynamic segment [${param}] to the route path or provide it in the headless component's YAML body.`
4294
5056
  );
4295
5057
  }
4296
5058
  }
@@ -4514,6 +5276,7 @@ async function runPluginValidators(projectRoot, parsedFiles, errors, warnings) {
4514
5276
  const ctx = {
4515
5277
  filePath: relativePath,
4516
5278
  body: parsed.body,
5279
+ css: parsed.css,
4517
5280
  head: parsed.headMeta,
4518
5281
  contract: resolvedPageContract ? {
4519
5282
  name: resolvedPageContract.name,
@@ -4660,7 +5423,13 @@ async function validateJayFiles(options = {}) {
4660
5423
  continue;
4661
5424
  }
4662
5425
  parsedFiles.push({ relativePath, parsed: parsedFile.val });
4663
- const routeParamWarnings = checkRouteParams(parsedFile.val, jayFile, scanDir, content);
5426
+ if (content.includes("application/jay-params")) {
5427
+ warnings.push({
5428
+ file: relativePath,
5429
+ message: '<script type="application/jay-params"> is deprecated. Move the values into the YAML body of the headless component that uses them. See agent-kit/developer/routing.md for details.'
5430
+ });
5431
+ }
5432
+ const routeParamWarnings = checkRouteParams(parsedFile.val, jayFile, scanDir);
4664
5433
  for (const msg of routeParamWarnings) {
4665
5434
  warnings.push({ file: relativePath, message: msg });
4666
5435
  }
@@ -4784,11 +5553,25 @@ function printJayValidationResult(result, options) {
4784
5553
  logger.important(chalk.blue(` Suggestion: ${error.suggestion}`));
4785
5554
  }
4786
5555
  }
5556
+ const fileGroups = /* @__PURE__ */ new Map();
4787
5557
  for (const warning of warns) {
4788
- logger.important(chalk.yellow(` ⚠ ${warning.file}`));
4789
- logger.important(chalk.gray(` ${warning.message}`));
4790
- if (warning.suggestion) {
4791
- logger.important(chalk.blue(` Suggestion: ${warning.suggestion}`));
5558
+ const group = fileGroups.get(warning.file) || [];
5559
+ group.push(warning);
5560
+ fileGroups.set(warning.file, group);
5561
+ }
5562
+ for (const [file, groupWarns] of fileGroups) {
5563
+ logger.important(chalk.yellow(` ⚠ ${file}`));
5564
+ for (const warning of groupWarns) {
5565
+ if (warning.message) {
5566
+ logger.important(chalk.gray(` ${warning.message}`));
5567
+ }
5568
+ }
5569
+ const suggestions = [...new Set(groupWarns.map((w) => w.suggestion).filter(Boolean))];
5570
+ if (suggestions.length > 0) {
5571
+ logger.important(chalk.blue(` Suggestions:`));
5572
+ for (const s2 of suggestions) {
5573
+ logger.important(chalk.blue(` ${s2}`));
5574
+ }
4792
5575
  }
4793
5576
  }
4794
5577
  }
@@ -4909,7 +5692,7 @@ async function runAgentKit(options) {
4909
5692
  await ensureAgentKitDocs(projectRoot, options.force, options.mode);
4910
5693
  await mergePluginAgentKitGuides(projectRoot, options.mode);
4911
5694
  if (options.references !== false) {
4912
- await generatePluginReferences(projectRoot, options, initErrors, viteServer);
5695
+ await generatePluginAgentKit(projectRoot, options, initErrors, viteServer);
4913
5696
  }
4914
5697
  }
4915
5698
  } finally {
@@ -5106,9 +5889,9 @@ async function mergePluginAgentKitGuides(projectRoot, mode) {
5106
5889
  await fs$1.appendFile(instructionsPath, lines.join("\n"));
5107
5890
  }
5108
5891
  }
5109
- async function generatePluginReferences(projectRoot, options, initErrors, viteServer) {
5110
- const { discoverPluginsWithReferences, executePluginReferences } = await import("@jay-framework/stack-server-runtime");
5111
- const plugins = await discoverPluginsWithReferences({
5892
+ async function generatePluginAgentKit(projectRoot, options, initErrors, viteServer) {
5893
+ const { discoverPluginsWithAgentKit, executePluginAgentKit } = await import("@jay-framework/stack-server-runtime");
5894
+ const plugins = await discoverPluginsWithAgentKit({
5112
5895
  projectRoot,
5113
5896
  verbose: options.verbose,
5114
5897
  pluginFilter: options.plugin
@@ -5117,35 +5900,35 @@ async function generatePluginReferences(projectRoot, options, initErrors, viteSe
5117
5900
  return;
5118
5901
  const logger = getLogger();
5119
5902
  logger.important("");
5120
- logger.important(chalk.bold("Generating plugin references..."));
5903
+ logger.important(chalk.bold("Generating plugin agent-kit data..."));
5121
5904
  for (const plugin of plugins) {
5122
5905
  const pluginInitError = initErrors.get(plugin.name);
5123
5906
  if (pluginInitError) {
5124
5907
  logger.warn(
5125
5908
  chalk.yellow(
5126
- ` ${plugin.name}: references skipped — init failed: ${pluginInitError.message}`
5909
+ ` ${plugin.name}: agent-kit skipped — init failed: ${pluginInitError.message}`
5127
5910
  )
5128
5911
  );
5129
5912
  continue;
5130
5913
  }
5131
5914
  try {
5132
- const result = await executePluginReferences(plugin, {
5915
+ const result = await executePluginAgentKit(plugin, {
5133
5916
  projectRoot,
5134
5917
  force: options.force ?? false,
5135
5918
  viteServer,
5136
5919
  verbose: options.verbose
5137
5920
  });
5138
- if (result.referencesCreated.length > 0) {
5921
+ if (result.agentKitCreated.length > 0) {
5139
5922
  logger.important(chalk.green(` ${plugin.name}:`));
5140
- for (const ref of result.referencesCreated) {
5141
- logger.important(chalk.gray(` ${ref}`));
5923
+ for (const created of result.agentKitCreated) {
5924
+ logger.important(chalk.gray(` ${created}`));
5142
5925
  }
5143
5926
  if (result.message) {
5144
5927
  logger.important(chalk.gray(` ${result.message}`));
5145
5928
  }
5146
5929
  }
5147
5930
  } catch (error) {
5148
- logger.warn(chalk.yellow(` ${plugin.name}: references skipped — ${error.message}`));
5931
+ logger.warn(chalk.yellow(` ${plugin.name}: agent-kit skipped — ${error.message}`));
5149
5932
  }
5150
5933
  }
5151
5934
  }