llmist 16.1.0 → 16.2.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
@@ -2,6 +2,12 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
+ }) : x)(function(x) {
8
+ if (typeof require !== "undefined") return require.apply(this, arguments);
9
+ throw Error('Dynamic require of "' + x + '" is not supported');
10
+ });
5
11
  var __esm = (fn, res) => function __init() {
6
12
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
13
  };
@@ -347,15 +353,15 @@ var init_execution_tree = __esm({
347
353
  const parentId = params.parentId ?? this.parentNodeId;
348
354
  const parent = parentId ? this.nodes.get(parentId) : null;
349
355
  const depth = parent ? parent.depth + 1 : this.baseDepth;
350
- const path = parent ? [...parent.path] : [];
356
+ const path3 = parent ? [...parent.path] : [];
351
357
  const id = this.generateLLMCallId(params.iteration, parentId);
352
- path.push(id);
358
+ path3.push(id);
353
359
  const node = {
354
360
  id,
355
361
  type: "llm_call",
356
362
  parentId,
357
363
  depth,
358
- path,
364
+ path: path3,
359
365
  createdAt: Date.now(),
360
366
  completedAt: null,
361
367
  iteration: params.iteration,
@@ -466,15 +472,15 @@ var init_execution_tree = __esm({
466
472
  const parentId = params.parentId ?? this.getCurrentLLMCallId() ?? this.parentNodeId;
467
473
  const parent = parentId ? this.nodes.get(parentId) : null;
468
474
  const depth = parent ? parent.depth + 1 : this.baseDepth;
469
- const path = parent ? [...parent.path] : [];
475
+ const path3 = parent ? [...parent.path] : [];
470
476
  const id = this.generateGadgetId(params.invocationId);
471
- path.push(id);
477
+ path3.push(id);
472
478
  const node = {
473
479
  id,
474
480
  type: "gadget",
475
481
  parentId,
476
482
  depth,
477
- path,
483
+ path: path3,
478
484
  createdAt: Date.now(),
479
485
  completedAt: null,
480
486
  invocationId: params.invocationId,
@@ -1413,8 +1419,8 @@ ${this.endPrefix}`
1413
1419
  });
1414
1420
  if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
1415
1421
  const idRefs = media.map((m, i) => {
1416
- const path = storedMedia?.[i]?.path;
1417
- const pathInfo = path ? ` \u2192 saved to: ${path}` : "";
1422
+ const path3 = storedMedia?.[i]?.path;
1423
+ const pathInfo = path3 ? ` \u2192 saved to: ${path3}` : "";
1418
1424
  return `[Media: ${mediaIds[i]} (${m.kind})${pathInfo}]`;
1419
1425
  }).join("\n");
1420
1426
  const textWithIds = `Result (${invocationId}): ${result}
@@ -4022,29 +4028,29 @@ function schemaToJSONSchema(schema, options) {
4022
4028
  }
4023
4029
  function detectDescriptionMismatch(schema, jsonSchema) {
4024
4030
  const mismatches = [];
4025
- function checkSchema(zodSchema, json, path) {
4031
+ function checkSchema(zodSchema, json, path3) {
4026
4032
  if (!zodSchema || typeof zodSchema !== "object") return;
4027
4033
  const def = zodSchema._def;
4028
4034
  const jsonObj = json;
4029
4035
  if (def?.description && !jsonObj?.description) {
4030
- mismatches.push(path || "root");
4036
+ mismatches.push(path3 || "root");
4031
4037
  }
4032
4038
  if (def?.typeName === "ZodObject" && def?.shape) {
4033
4039
  const shape = typeof def.shape === "function" ? def.shape() : def.shape;
4034
4040
  for (const [key, fieldSchema] of Object.entries(shape)) {
4035
4041
  const properties = jsonObj?.properties;
4036
4042
  const jsonProp = properties?.[key];
4037
- checkSchema(fieldSchema, jsonProp, path ? `${path}.${key}` : key);
4043
+ checkSchema(fieldSchema, jsonProp, path3 ? `${path3}.${key}` : key);
4038
4044
  }
4039
4045
  }
4040
4046
  if (def?.typeName === "ZodArray" && def?.type) {
4041
- checkSchema(def.type, jsonObj?.items, path ? `${path}[]` : "[]");
4047
+ checkSchema(def.type, jsonObj?.items, path3 ? `${path3}[]` : "[]");
4042
4048
  }
4043
4049
  if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
4044
- checkSchema(def.innerType, json, path);
4050
+ checkSchema(def.innerType, json, path3);
4045
4051
  }
4046
4052
  if (def?.typeName === "ZodDefault" && def?.innerType) {
4047
- checkSchema(def.innerType, json, path);
4053
+ checkSchema(def.innerType, json, path3);
4048
4054
  }
4049
4055
  }
4050
4056
  checkSchema(schema, jsonSchema, "");
@@ -4136,7 +4142,7 @@ Example fixes:
4136
4142
  );
4137
4143
  }
4138
4144
  }
4139
- function findUnknownTypes(schema, path = []) {
4145
+ function findUnknownTypes(schema, path3 = []) {
4140
4146
  const issues = [];
4141
4147
  if (!schema || typeof schema !== "object") {
4142
4148
  return issues;
@@ -4148,7 +4154,7 @@ function findUnknownTypes(schema, path = []) {
4148
4154
  }
4149
4155
  if (schema.properties) {
4150
4156
  for (const [propName, propSchema] of Object.entries(schema.properties)) {
4151
- const propPath = [...path, propName];
4157
+ const propPath = [...path3, propName];
4152
4158
  if (hasNoType(propSchema)) {
4153
4159
  issues.push(propPath.join(".") || propName);
4154
4160
  }
@@ -4156,7 +4162,7 @@ function findUnknownTypes(schema, path = []) {
4156
4162
  }
4157
4163
  }
4158
4164
  if (schema.items) {
4159
- const itemPath = [...path, "[]"];
4165
+ const itemPath = [...path3, "[]"];
4160
4166
  if (hasNoType(schema.items)) {
4161
4167
  issues.push(itemPath.join("."));
4162
4168
  }
@@ -4164,17 +4170,17 @@ function findUnknownTypes(schema, path = []) {
4164
4170
  }
4165
4171
  if (schema.anyOf) {
4166
4172
  schema.anyOf.forEach((subSchema, index) => {
4167
- issues.push(...findUnknownTypes(subSchema, [...path, `anyOf[${index}]`]));
4173
+ issues.push(...findUnknownTypes(subSchema, [...path3, `anyOf[${index}]`]));
4168
4174
  });
4169
4175
  }
4170
4176
  if (schema.oneOf) {
4171
4177
  schema.oneOf.forEach((subSchema, index) => {
4172
- issues.push(...findUnknownTypes(subSchema, [...path, `oneOf[${index}]`]));
4178
+ issues.push(...findUnknownTypes(subSchema, [...path3, `oneOf[${index}]`]));
4173
4179
  });
4174
4180
  }
4175
4181
  if (schema.allOf) {
4176
4182
  schema.allOf.forEach((subSchema, index) => {
4177
- issues.push(...findUnknownTypes(subSchema, [...path, `allOf[${index}]`]));
4183
+ issues.push(...findUnknownTypes(subSchema, [...path3, `allOf[${index}]`]));
4178
4184
  });
4179
4185
  }
4180
4186
  return issues;
@@ -5190,6 +5196,625 @@ var init_registry = __esm({
5190
5196
  }
5191
5197
  });
5192
5198
 
5199
+ // src/skills/activation.ts
5200
+ import { execSync } from "child_process";
5201
+ function substituteArguments(instructions, args) {
5202
+ if (!args) {
5203
+ return instructions.replace(/\$ARGUMENTS\[\d+\]/g, "").replace(/\$ARGUMENTS/g, "").replace(/\$\d+/g, "");
5204
+ }
5205
+ const parts = splitArguments(args);
5206
+ let result = instructions.replace(/\$ARGUMENTS\[(\d+)\]/g, (_match, index) => {
5207
+ const i = Number.parseInt(index, 10);
5208
+ return parts[i] ?? "";
5209
+ });
5210
+ result = result.replace(/\$ARGUMENTS/g, args);
5211
+ result = result.replace(/\$(\d+)/g, (_match, index) => {
5212
+ const i = Number.parseInt(index, 10);
5213
+ return parts[i] ?? "";
5214
+ });
5215
+ return result;
5216
+ }
5217
+ function preprocessShellCommands(instructions, options) {
5218
+ const { cwd, shell = "bash", timeoutMs = 1e4 } = options ?? {};
5219
+ return instructions.replace(/!`([^`]+)`/g, (_match, command) => {
5220
+ try {
5221
+ const output = execSync(command, {
5222
+ cwd,
5223
+ shell: shell === "powershell" ? "powershell" : "/bin/bash",
5224
+ timeout: timeoutMs,
5225
+ encoding: "utf-8",
5226
+ stdio: ["pipe", "pipe", "pipe"]
5227
+ });
5228
+ return output.trim();
5229
+ } catch (error) {
5230
+ const msg = error instanceof Error ? error.message : String(error);
5231
+ return `[Error executing \`${command}\`: ${msg}]`;
5232
+ }
5233
+ });
5234
+ }
5235
+ function substituteVariables(instructions, variables) {
5236
+ return instructions.replace(/\$\{(\w+)\}/g, (_match, varName) => {
5237
+ return variables[varName] ?? "";
5238
+ });
5239
+ }
5240
+ function resolveInstructions(instructions, options) {
5241
+ let resolved = instructions;
5242
+ if (options?.variables) {
5243
+ resolved = substituteVariables(resolved, options.variables);
5244
+ }
5245
+ resolved = substituteArguments(resolved, options?.arguments);
5246
+ if (options?.enableShellPreprocessing !== false) {
5247
+ resolved = preprocessShellCommands(resolved, {
5248
+ cwd: options?.cwd,
5249
+ shell: options?.shell,
5250
+ timeoutMs: options?.shellTimeoutMs
5251
+ });
5252
+ }
5253
+ return resolved;
5254
+ }
5255
+ function splitArguments(args) {
5256
+ const parts = [];
5257
+ let current = "";
5258
+ let inQuote = null;
5259
+ for (const char of args) {
5260
+ if (inQuote) {
5261
+ if (char === inQuote) {
5262
+ inQuote = null;
5263
+ } else {
5264
+ current += char;
5265
+ }
5266
+ } else if (char === '"' || char === "'") {
5267
+ inQuote = char;
5268
+ } else if (char === " " || char === " ") {
5269
+ if (current) {
5270
+ parts.push(current);
5271
+ current = "";
5272
+ }
5273
+ } else {
5274
+ current += char;
5275
+ }
5276
+ }
5277
+ if (current) parts.push(current);
5278
+ return parts;
5279
+ }
5280
+ var init_activation = __esm({
5281
+ "src/skills/activation.ts"() {
5282
+ "use strict";
5283
+ }
5284
+ });
5285
+
5286
+ // src/skills/parser.ts
5287
+ import fs from "fs";
5288
+ import path from "path";
5289
+ import yaml from "js-yaml";
5290
+ function parseFrontmatter(content) {
5291
+ const trimmed = content.trimStart();
5292
+ if (!trimmed.startsWith("---")) {
5293
+ return { frontmatter: {}, body: content };
5294
+ }
5295
+ const endIndex = trimmed.indexOf("\n---", 3);
5296
+ if (endIndex === -1) {
5297
+ return { frontmatter: {}, body: content };
5298
+ }
5299
+ const yamlBlock = trimmed.slice(3, endIndex).trim();
5300
+ const body = trimmed.slice(endIndex + 4).trim();
5301
+ const parsed = yaml.load(yamlBlock);
5302
+ const frontmatter = typeof parsed === "object" && parsed !== null ? parsed : {};
5303
+ return { frontmatter, body };
5304
+ }
5305
+ function parseMetadata(frontmatter, fallbackName) {
5306
+ const name = parseString(frontmatter.name) ?? fallbackName ?? "unnamed-skill";
5307
+ const description = parseString(frontmatter.description) ?? "";
5308
+ return {
5309
+ name,
5310
+ description,
5311
+ argumentHint: parseString(frontmatter["argument-hint"]),
5312
+ allowedTools: parseStringArray(frontmatter["allowed-tools"]),
5313
+ model: parseString(frontmatter.model),
5314
+ context: parseContext(frontmatter.context),
5315
+ agent: parseString(frontmatter.agent),
5316
+ paths: parseStringArray(frontmatter.paths),
5317
+ gadgets: parseStringArray(frontmatter.gadgets),
5318
+ disableModelInvocation: parseBool(frontmatter["disable-model-invocation"]),
5319
+ userInvocable: parseBool(frontmatter["user-invocable"]),
5320
+ shell: parseShell(frontmatter.shell),
5321
+ version: parseString(frontmatter.version)
5322
+ };
5323
+ }
5324
+ function scanResources(skillDir) {
5325
+ const resources = [];
5326
+ for (const category of RESOURCE_CATEGORIES) {
5327
+ const categoryDir = path.join(skillDir, category);
5328
+ if (!fs.existsSync(categoryDir)) continue;
5329
+ const stat = fs.statSync(categoryDir);
5330
+ if (!stat.isDirectory()) continue;
5331
+ for (const file of walkDirectory(categoryDir)) {
5332
+ resources.push({
5333
+ relativePath: path.relative(skillDir, file),
5334
+ absolutePath: file,
5335
+ category
5336
+ });
5337
+ }
5338
+ }
5339
+ return resources;
5340
+ }
5341
+ function parseSkillFile(skillMdPath, source, loadInstructions = false) {
5342
+ const content = fs.readFileSync(skillMdPath, "utf-8");
5343
+ return parseSkillContent(content, skillMdPath, source, loadInstructions);
5344
+ }
5345
+ function parseSkillContent(content, sourcePath, source, loadInstructions = false) {
5346
+ const sourceDir = path.dirname(sourcePath);
5347
+ const fallbackName = path.basename(sourceDir);
5348
+ const { frontmatter, body } = parseFrontmatter(content);
5349
+ const metadata = parseMetadata(frontmatter, fallbackName);
5350
+ const resources = fs.existsSync(sourceDir) ? scanResources(sourceDir) : [];
5351
+ return {
5352
+ metadata,
5353
+ instructions: loadInstructions ? body : null,
5354
+ resources,
5355
+ sourcePath,
5356
+ sourceDir,
5357
+ source
5358
+ };
5359
+ }
5360
+ function validateMetadata(metadata) {
5361
+ const issues = [];
5362
+ if (!metadata.name) {
5363
+ issues.push("Skill name is required");
5364
+ } else {
5365
+ if (metadata.name.length > MAX_NAME_LENGTH) {
5366
+ issues.push(`Skill name exceeds ${MAX_NAME_LENGTH} characters`);
5367
+ }
5368
+ if (!NAME_PATTERN.test(metadata.name)) {
5369
+ issues.push(
5370
+ "Skill name must contain only lowercase letters, numbers, and hyphens, and must start with a letter or number"
5371
+ );
5372
+ }
5373
+ }
5374
+ if (!metadata.description) {
5375
+ issues.push("Skill description is required");
5376
+ } else if (metadata.description.length > MAX_DESCRIPTION_LENGTH) {
5377
+ issues.push(`Skill description exceeds ${MAX_DESCRIPTION_LENGTH} characters`);
5378
+ }
5379
+ if (metadata.context && metadata.context !== "fork" && metadata.context !== "inline") {
5380
+ issues.push('Skill context must be "fork" or "inline"');
5381
+ }
5382
+ return issues;
5383
+ }
5384
+ function parseString(value) {
5385
+ if (value == null) return void 0;
5386
+ if (typeof value === "string") return value;
5387
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
5388
+ return void 0;
5389
+ }
5390
+ function parseStringArray(value) {
5391
+ if (value == null) return void 0;
5392
+ if (Array.isArray(value)) {
5393
+ return value.filter((v) => typeof v === "string" || typeof v === "number").map(String);
5394
+ }
5395
+ if (typeof value === "string") return [value];
5396
+ return void 0;
5397
+ }
5398
+ function parseContext(value) {
5399
+ if (value === "fork" || value === "inline") return value;
5400
+ return void 0;
5401
+ }
5402
+ function parseShell(value) {
5403
+ if (value === "bash" || value === "powershell") return value;
5404
+ return void 0;
5405
+ }
5406
+ function parseBool(value) {
5407
+ if (value === true || value === false) return value;
5408
+ if (value === "true") return true;
5409
+ if (value === "false") return false;
5410
+ return void 0;
5411
+ }
5412
+ function* walkDirectory(dir) {
5413
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
5414
+ for (const entry of entries) {
5415
+ const fullPath = path.join(dir, entry.name);
5416
+ if (entry.isDirectory()) {
5417
+ yield* walkDirectory(fullPath);
5418
+ } else if (entry.isFile()) {
5419
+ yield fullPath;
5420
+ }
5421
+ }
5422
+ }
5423
+ var RESOURCE_CATEGORIES, MAX_NAME_LENGTH, MAX_DESCRIPTION_LENGTH, NAME_PATTERN;
5424
+ var init_parser = __esm({
5425
+ "src/skills/parser.ts"() {
5426
+ "use strict";
5427
+ RESOURCE_CATEGORIES = ["scripts", "references", "assets"];
5428
+ MAX_NAME_LENGTH = 64;
5429
+ MAX_DESCRIPTION_LENGTH = 1024;
5430
+ NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
5431
+ }
5432
+ });
5433
+
5434
+ // src/skills/registry.ts
5435
+ var registry_exports = {};
5436
+ __export(registry_exports, {
5437
+ SkillRegistry: () => SkillRegistry
5438
+ });
5439
+ import { minimatch } from "minimatch";
5440
+ var DEFAULT_CHAR_BUDGET, SUMMARY_DESCRIPTION_LIMIT, SkillRegistry;
5441
+ var init_registry2 = __esm({
5442
+ "src/skills/registry.ts"() {
5443
+ "use strict";
5444
+ DEFAULT_CHAR_BUDGET = 8e3;
5445
+ SUMMARY_DESCRIPTION_LIMIT = 250;
5446
+ SkillRegistry = class _SkillRegistry {
5447
+ skills = /* @__PURE__ */ new Map();
5448
+ /**
5449
+ * Register a skill. Overwrites any existing skill with the same name.
5450
+ *
5451
+ * Unlike GadgetRegistry (which throws on duplicates), SkillRegistry allows
5452
+ * overwriting because skills are loaded from multiple sources with intentional
5453
+ * priority ordering (project > user > default).
5454
+ */
5455
+ register(skill) {
5456
+ this.skills.set(skill.name.toLowerCase(), skill);
5457
+ }
5458
+ /** Register multiple skills. */
5459
+ registerMany(skills) {
5460
+ for (const skill of skills) {
5461
+ this.register(skill);
5462
+ }
5463
+ }
5464
+ /** Remove a skill by name (case-insensitive). Returns true if removed. */
5465
+ remove(name) {
5466
+ return this.skills.delete(name.toLowerCase());
5467
+ }
5468
+ /** Remove all registered skills. */
5469
+ clear() {
5470
+ this.skills.clear();
5471
+ }
5472
+ /** Get a skill by name (case-insensitive). */
5473
+ get(name) {
5474
+ return this.skills.get(name.toLowerCase());
5475
+ }
5476
+ /** Check if a skill exists by name (case-insensitive). */
5477
+ has(name) {
5478
+ return this.skills.has(name.toLowerCase());
5479
+ }
5480
+ /** Get all registered skills. */
5481
+ getAll() {
5482
+ return [...this.skills.values()];
5483
+ }
5484
+ /** Get all skill names. */
5485
+ getNames() {
5486
+ return [...this.skills.keys()];
5487
+ }
5488
+ /** Number of registered skills. */
5489
+ get size() {
5490
+ return this.skills.size;
5491
+ }
5492
+ /**
5493
+ * Get skills that are visible to the LLM for auto-triggering.
5494
+ * Excludes skills with disableModelInvocation: true.
5495
+ */
5496
+ getModelInvocable() {
5497
+ return this.getAll().filter((s) => s.isModelInvocable);
5498
+ }
5499
+ /**
5500
+ * Get skills that the user can invoke via /skill-name.
5501
+ * Excludes skills with userInvocable: false.
5502
+ */
5503
+ getUserInvocable() {
5504
+ return this.getAll().filter((s) => s.isUserInvocable);
5505
+ }
5506
+ /**
5507
+ * Generate metadata summaries for system prompt injection (Tier 1).
5508
+ *
5509
+ * Each skill contributes a one-line summary: "name — description".
5510
+ * Output is truncated to fit the character budget.
5511
+ *
5512
+ * @param charBudget - Maximum characters for all summaries combined.
5513
+ */
5514
+ getMetadataSummaries(charBudget = DEFAULT_CHAR_BUDGET) {
5515
+ const invocable = this.getModelInvocable();
5516
+ if (invocable.length === 0) return "";
5517
+ const lines = [];
5518
+ let totalChars = 0;
5519
+ for (const skill of invocable) {
5520
+ const desc = skill.description.length > SUMMARY_DESCRIPTION_LIMIT ? `${skill.description.slice(0, SUMMARY_DESCRIPTION_LIMIT - 3)}...` : skill.description;
5521
+ const line = `- ${skill.name}: ${desc}`;
5522
+ if (totalChars + line.length > charBudget) break;
5523
+ lines.push(line);
5524
+ totalChars += line.length + 1;
5525
+ }
5526
+ return lines.join("\n");
5527
+ }
5528
+ /**
5529
+ * Find skills whose `paths` patterns match a given file path.
5530
+ * Used for auto-activation when the user is working on specific files.
5531
+ */
5532
+ findByFilePath(filePath) {
5533
+ return this.getModelInvocable().filter((skill) => {
5534
+ const patterns = skill.metadata.paths;
5535
+ if (!patterns || patterns.length === 0) return false;
5536
+ return patterns.some((pattern) => minimatch(filePath, pattern));
5537
+ });
5538
+ }
5539
+ /**
5540
+ * Merge another registry into this one.
5541
+ * Skills from the other registry overwrite existing skills with the same name.
5542
+ */
5543
+ merge(other) {
5544
+ for (const skill of other.getAll()) {
5545
+ this.register(skill);
5546
+ }
5547
+ }
5548
+ /** Create a registry from an array of skills. */
5549
+ static from(skills) {
5550
+ const registry = new _SkillRegistry();
5551
+ registry.registerMany(skills);
5552
+ return registry;
5553
+ }
5554
+ };
5555
+ }
5556
+ });
5557
+
5558
+ // src/skills/skill.ts
5559
+ import fs2 from "fs/promises";
5560
+ var Skill;
5561
+ var init_skill = __esm({
5562
+ "src/skills/skill.ts"() {
5563
+ "use strict";
5564
+ init_activation();
5565
+ init_parser();
5566
+ Skill = class _Skill {
5567
+ metadata;
5568
+ sourcePath;
5569
+ sourceDir;
5570
+ source;
5571
+ _instructions;
5572
+ _resources;
5573
+ _resourceCache = /* @__PURE__ */ new Map();
5574
+ _resourceLoading = /* @__PURE__ */ new Map();
5575
+ constructor(parsed) {
5576
+ this.metadata = parsed.metadata;
5577
+ this.sourcePath = parsed.sourcePath;
5578
+ this.sourceDir = parsed.sourceDir;
5579
+ this.source = parsed.source;
5580
+ this._instructions = parsed.instructions;
5581
+ this._resources = parsed.resources;
5582
+ }
5583
+ /** Skill name for registry lookup. */
5584
+ get name() {
5585
+ return this.metadata.name;
5586
+ }
5587
+ /** Skill description for LLM matching. */
5588
+ get description() {
5589
+ return this.metadata.description;
5590
+ }
5591
+ /** Whether the LLM can auto-trigger this skill. */
5592
+ get isModelInvocable() {
5593
+ return this.metadata.disableModelInvocation !== true;
5594
+ }
5595
+ /** Whether the user can invoke this skill via /skill-name. */
5596
+ get isUserInvocable() {
5597
+ return this.metadata.userInvocable !== false;
5598
+ }
5599
+ /**
5600
+ * Load and cache Tier 2 instructions.
5601
+ * If instructions were loaded during parsing, returns the cached value.
5602
+ */
5603
+ async getInstructions() {
5604
+ if (this._instructions !== null) return this._instructions;
5605
+ const content = await fs2.readFile(this.sourcePath, "utf-8");
5606
+ const { body } = parseFrontmatter(content);
5607
+ this._instructions = body;
5608
+ return body;
5609
+ }
5610
+ /**
5611
+ * List Tier 3 resources.
5612
+ * Resources are discovered at parse time but content is loaded on demand.
5613
+ */
5614
+ getResources() {
5615
+ return this._resources;
5616
+ }
5617
+ /**
5618
+ * Load a specific Tier 3 resource by relative path.
5619
+ * Results are cached for the lifetime of this Skill instance.
5620
+ * Concurrent calls for the same resource share a single read.
5621
+ */
5622
+ async getResource(relativePath) {
5623
+ if (relativePath.includes("..")) {
5624
+ throw new Error(`Invalid resource path (path traversal): ${relativePath}`);
5625
+ }
5626
+ const cached = this._resourceCache.get(relativePath);
5627
+ if (cached !== void 0) return cached;
5628
+ const existing = this._resourceLoading.get(relativePath);
5629
+ if (existing) return existing;
5630
+ const resource = this._resources.find((r) => r.relativePath === relativePath);
5631
+ if (!resource) {
5632
+ throw new Error(`Resource not found: ${relativePath} in skill ${this.name}`);
5633
+ }
5634
+ const loadPromise = fs2.readFile(resource.absolutePath, "utf-8").then(
5635
+ (content) => {
5636
+ this._resourceCache.set(relativePath, content);
5637
+ this._resourceLoading.delete(relativePath);
5638
+ return content;
5639
+ },
5640
+ (error) => {
5641
+ this._resourceLoading.delete(relativePath);
5642
+ throw new Error(
5643
+ `Failed to load resource ${relativePath} in skill ${this.name}: ${error instanceof Error ? error.message : String(error)}`
5644
+ );
5645
+ }
5646
+ );
5647
+ this._resourceLoading.set(relativePath, loadPromise);
5648
+ return loadPromise;
5649
+ }
5650
+ /**
5651
+ * Activate this skill with optional arguments.
5652
+ *
5653
+ * Performs:
5654
+ * 1. Variable substitution (${SKILL_DIR}, etc.)
5655
+ * 2. Argument substitution ($ARGUMENTS, $0, $1)
5656
+ * 3. Shell preprocessing (!`command`)
5657
+ * 4. Resource loading (if eagerResources is true)
5658
+ */
5659
+ async activate(options) {
5660
+ const instructions = await this.getInstructions();
5661
+ const resolvedInstructions = resolveInstructions(instructions, {
5662
+ arguments: options?.arguments,
5663
+ variables: {
5664
+ SKILL_DIR: this.sourceDir,
5665
+ CLAUDE_SKILL_DIR: this.sourceDir
5666
+ },
5667
+ cwd: options?.cwd ?? this.sourceDir,
5668
+ shell: this.metadata.shell,
5669
+ enableShellPreprocessing: options?.enableShellPreprocessing,
5670
+ shellTimeoutMs: options?.shellTimeoutMs
5671
+ });
5672
+ const loadedResources = /* @__PURE__ */ new Map();
5673
+ if (options?.eagerResources) {
5674
+ for (const resource of this._resources) {
5675
+ const content = await this.getResource(resource.relativePath);
5676
+ loadedResources.set(resource.relativePath, content);
5677
+ }
5678
+ }
5679
+ return {
5680
+ skillName: this.name,
5681
+ resolvedInstructions,
5682
+ gadgets: [],
5683
+ // Gadgets are resolved by the CLI layer
5684
+ loadedResources
5685
+ };
5686
+ }
5687
+ /**
5688
+ * Create a Skill from a SKILL.md content string.
5689
+ * Useful for testing or dynamic skill creation.
5690
+ */
5691
+ static fromContent(content, sourcePath, source = { type: "directory", path: sourcePath }) {
5692
+ const parsed = parseSkillContent(content, sourcePath, source, true);
5693
+ return new _Skill(parsed);
5694
+ }
5695
+ };
5696
+ }
5697
+ });
5698
+
5699
+ // src/skills/loader.ts
5700
+ import fs3 from "fs";
5701
+ import os from "os";
5702
+ import path2 from "path";
5703
+ function loadSkillsFromDirectory(dir, source, onWarning) {
5704
+ if (!fs3.existsSync(dir)) return [];
5705
+ const stat = fs3.statSync(dir);
5706
+ if (!stat.isDirectory()) return [];
5707
+ const skills = [];
5708
+ scanForSkills(dir, source, skills, onWarning);
5709
+ return skills;
5710
+ }
5711
+ function discoverSkills(options) {
5712
+ const registry = new SkillRegistry();
5713
+ const userSkillsDir = options?.userDir ?? path2.join(os.homedir(), CONFIG_DIR_NAME, SKILLS_DIR_NAME);
5714
+ const userSkills = loadSkillsFromDirectory(userSkillsDir, {
5715
+ type: "user",
5716
+ path: userSkillsDir
5717
+ });
5718
+ registry.registerMany(userSkills);
5719
+ if (options?.projectDir) {
5720
+ const projectSkillsDir = path2.join(options.projectDir, CONFIG_DIR_NAME, SKILLS_DIR_NAME);
5721
+ const projectSkills = loadSkillsFromDirectory(projectSkillsDir, {
5722
+ type: "project",
5723
+ path: projectSkillsDir
5724
+ });
5725
+ registry.registerMany(projectSkills);
5726
+ }
5727
+ if (options?.additionalDirs) {
5728
+ for (const dir of options.additionalDirs) {
5729
+ const resolvedDir = dir.startsWith("~") ? path2.join(os.homedir(), dir.slice(1)) : dir;
5730
+ const skills = loadSkillsFromDirectory(resolvedDir, {
5731
+ type: "directory",
5732
+ path: resolvedDir
5733
+ });
5734
+ registry.registerMany(skills);
5735
+ }
5736
+ }
5737
+ return registry;
5738
+ }
5739
+ function scanForSkills(dir, source, results, onWarning) {
5740
+ let entries;
5741
+ try {
5742
+ entries = fs3.readdirSync(dir, { withFileTypes: true });
5743
+ } catch (error) {
5744
+ onWarning?.(
5745
+ `Cannot read skill directory ${dir}: ${error instanceof Error ? error.message : String(error)}`
5746
+ );
5747
+ return;
5748
+ }
5749
+ const skillMdPath = path2.join(dir, "SKILL.md");
5750
+ if (fs3.existsSync(skillMdPath)) {
5751
+ try {
5752
+ const parsed = parseSkillFile(skillMdPath, source, false);
5753
+ results.push(new Skill(parsed));
5754
+ } catch (error) {
5755
+ onWarning?.(
5756
+ `Failed to parse ${skillMdPath}: ${error instanceof Error ? error.message : String(error)}`
5757
+ );
5758
+ }
5759
+ return;
5760
+ }
5761
+ for (const entry of entries) {
5762
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
5763
+ scanForSkills(path2.join(dir, entry.name), source, results, onWarning);
5764
+ }
5765
+ }
5766
+ }
5767
+ var SKILLS_DIR_NAME, CONFIG_DIR_NAME;
5768
+ var init_loader = __esm({
5769
+ "src/skills/loader.ts"() {
5770
+ "use strict";
5771
+ init_parser();
5772
+ init_registry2();
5773
+ init_skill();
5774
+ SKILLS_DIR_NAME = "skills";
5775
+ CONFIG_DIR_NAME = ".llmist";
5776
+ }
5777
+ });
5778
+
5779
+ // src/skills/use-skill-gadget.ts
5780
+ import { z as z4 } from "zod";
5781
+ function createUseSkillGadget(registry) {
5782
+ const summaries = registry.getMetadataSummaries();
5783
+ const skillNames = registry.getModelInvocable().map((s) => s.name);
5784
+ const description = [
5785
+ "Activate a skill to get specialized instructions for a task.",
5786
+ "Available skills:",
5787
+ summaries
5788
+ ].join("\n");
5789
+ return createGadget({
5790
+ name: USE_SKILL_GADGET_NAME,
5791
+ description,
5792
+ schema: z4.object({
5793
+ skill: z4.enum(skillNames).describe("Name of the skill to activate"),
5794
+ arguments: z4.string().optional().describe("Arguments for the skill (e.g., a filename, issue number, or search query)")
5795
+ }),
5796
+ execute: async ({ skill: skillName, arguments: args }) => {
5797
+ const skill = registry.get(skillName);
5798
+ if (!skill) {
5799
+ return `Unknown skill: "${skillName}". Available skills: ${skillNames.join(", ")}`;
5800
+ }
5801
+ const activation = await skill.activate({
5802
+ arguments: args,
5803
+ cwd: process.cwd()
5804
+ });
5805
+ return activation.resolvedInstructions;
5806
+ }
5807
+ });
5808
+ }
5809
+ var USE_SKILL_GADGET_NAME;
5810
+ var init_use_skill_gadget = __esm({
5811
+ "src/skills/use-skill-gadget.ts"() {
5812
+ "use strict";
5813
+ init_create_gadget();
5814
+ USE_SKILL_GADGET_NAME = "UseSkill";
5815
+ }
5816
+ });
5817
+
5193
5818
  // src/agent/builder-utils.ts
5194
5819
  function formatGadgetCall(gadgetName, invocationId, parameters, prefixes) {
5195
5820
  const startPrefix = prefixes?.start ?? GADGET_START_PREFIX;
@@ -12194,6 +12819,10 @@ var init_builder = __esm({
12194
12819
  "use strict";
12195
12820
  init_model_shortcuts();
12196
12821
  init_registry();
12822
+ init_activation();
12823
+ init_loader();
12824
+ init_parser();
12825
+ init_use_skill_gadget();
12197
12826
  init_agent();
12198
12827
  init_agent_internal_key();
12199
12828
  init_builder_utils();
@@ -12205,12 +12834,14 @@ var init_builder = __esm({
12205
12834
  retry;
12206
12835
  subagents;
12207
12836
  policies;
12837
+ skills;
12208
12838
  constructor(client) {
12209
12839
  this.core = { client, initialMessages: [] };
12210
12840
  this.gadgets = { gadgets: [] };
12211
12841
  this.retry = {};
12212
12842
  this.subagents = {};
12213
12843
  this.policies = {};
12844
+ this.skills = { preActivated: [], skillDirs: [] };
12214
12845
  }
12215
12846
  /** Set the model to use. Supports aliases like "sonnet", "flash". */
12216
12847
  withModel(model) {
@@ -12347,6 +12978,38 @@ var init_builder = __esm({
12347
12978
  this.policies.compactionConfig = { enabled: false };
12348
12979
  return this;
12349
12980
  }
12981
+ // ─── Skills ──────────────────────────────────────────────────────────────────
12982
+ /** Register a skill registry for this agent. */
12983
+ withSkills(registry) {
12984
+ this.skills.registry = registry;
12985
+ return this;
12986
+ }
12987
+ /**
12988
+ * Pre-activate a specific skill before the agent starts.
12989
+ * Instructions are injected into the system prompt.
12990
+ *
12991
+ * Note: each call replaces (not appends) the pre-activated skill for that name.
12992
+ * This is safe for REPL loops where the same builder is reused.
12993
+ */
12994
+ withSkill(name, args) {
12995
+ const existing = this.skills.preActivated.findIndex((s) => s.name === name);
12996
+ if (existing !== -1) {
12997
+ this.skills.preActivated[existing] = { name, args };
12998
+ } else {
12999
+ this.skills.preActivated.push({ name, args });
13000
+ }
13001
+ return this;
13002
+ }
13003
+ /** Clear all pre-activated skills. Call between REPL iterations. */
13004
+ clearPreActivatedSkills() {
13005
+ this.skills.preActivated = [];
13006
+ return this;
13007
+ }
13008
+ /** Add a directory to scan for skills. */
13009
+ withSkillsFrom(dir) {
13010
+ this.skills.skillDirs.push(dir);
13011
+ return this;
13012
+ }
12350
13013
  /** Configure retry behavior for LLM API calls. */
12351
13014
  withRetry(config) {
12352
13015
  this.retry.retryConfig = { ...config, enabled: config.enabled ?? true };
@@ -12438,16 +13101,75 @@ var init_builder = __esm({
12438
13101
  composeHooks() {
12439
13102
  return HookComposer.compose(this.core.hooks, this.core.trailingMessage);
12440
13103
  }
13104
+ resolveSkillRegistry() {
13105
+ if (this.skills.registry) {
13106
+ if (this.skills.skillDirs.length > 0) {
13107
+ for (const dir of this.skills.skillDirs) {
13108
+ const skills = loadSkillsFromDirectory(dir, { type: "directory", path: dir });
13109
+ this.skills.registry.registerMany(skills);
13110
+ }
13111
+ }
13112
+ return this.skills.registry;
13113
+ }
13114
+ if (this.skills.skillDirs.length > 0) {
13115
+ const { SkillRegistry: SR } = (init_registry2(), __toCommonJS(registry_exports));
13116
+ const reg = new SR();
13117
+ for (const dir of this.skills.skillDirs) {
13118
+ const skills = loadSkillsFromDirectory(dir, { type: "directory", path: dir });
13119
+ reg.registerMany(skills);
13120
+ }
13121
+ return reg;
13122
+ }
13123
+ return void 0;
13124
+ }
13125
+ /**
13126
+ * Resolve pre-activated skill instructions synchronously.
13127
+ * Reads SKILL.md from disk via readFileSync (skills are local files).
13128
+ */
13129
+ resolvePreActivatedInstructions(skillRegistry) {
13130
+ if (this.skills.preActivated.length === 0) return void 0;
13131
+ const fs4 = __require("fs");
13132
+ const blocks = [];
13133
+ for (const { name, args } of this.skills.preActivated) {
13134
+ const skill = skillRegistry.get(name);
13135
+ if (!skill) continue;
13136
+ const content = fs4.readFileSync(skill.sourcePath, "utf-8");
13137
+ const { body } = parseFrontmatter(content);
13138
+ const resolved = resolveInstructions(body, {
13139
+ arguments: args,
13140
+ variables: { SKILL_DIR: skill.sourceDir, CLAUDE_SKILL_DIR: skill.sourceDir },
13141
+ cwd: skill.sourceDir,
13142
+ shell: skill.metadata.shell
13143
+ });
13144
+ blocks.push(`## Skill: ${name}
13145
+
13146
+ ${resolved}`);
13147
+ }
13148
+ return blocks.length > 0 ? blocks.join("\n\n---\n\n") : void 0;
13149
+ }
12441
13150
  buildAgentOptions(userPrompt) {
12442
13151
  if (!this.core.client) {
12443
13152
  const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
12444
13153
  this.core.client = new LLMistClass();
12445
13154
  }
12446
13155
  const registry = GadgetRegistry.from(this.gadgets.gadgets);
13156
+ let systemPrompt = this.core.systemPrompt;
13157
+ const skillRegistry = this.resolveSkillRegistry();
13158
+ if (skillRegistry && skillRegistry.size > 0) {
13159
+ if (skillRegistry.getModelInvocable().length > 0) {
13160
+ registry.registerByClass(createUseSkillGadget(skillRegistry));
13161
+ }
13162
+ const preActivatedBlock = this.resolvePreActivatedInstructions(skillRegistry);
13163
+ if (preActivatedBlock) {
13164
+ systemPrompt = systemPrompt ? `${systemPrompt}
13165
+
13166
+ ${preActivatedBlock}` : preActivatedBlock;
13167
+ }
13168
+ }
12447
13169
  return {
12448
13170
  client: this.core.client,
12449
13171
  model: this.core.model ?? "openai:gpt-5-nano",
12450
- systemPrompt: this.core.systemPrompt,
13172
+ systemPrompt,
12451
13173
  userPrompt,
12452
13174
  registry,
12453
13175
  maxIterations: this.core.maxIterations,
@@ -13132,8 +13854,8 @@ var init_error_formatter = __esm({
13132
13854
  const parts = [];
13133
13855
  parts.push(`Error: Invalid parameters for '${gadgetName}':`);
13134
13856
  for (const issue of zodError.issues) {
13135
- const path = issue.path.join(".") || "root";
13136
- parts.push(` - ${path}: ${issue.message}`);
13857
+ const path3 = issue.path.join(".") || "root";
13858
+ parts.push(` - ${path3}: ${issue.message}`);
13137
13859
  }
13138
13860
  parts.push("");
13139
13861
  parts.push("Gadget Usage:");
@@ -13198,7 +13920,7 @@ function stripMarkdownFences(content) {
13198
13920
  return cleaned.trim();
13199
13921
  }
13200
13922
  var globalInvocationCounter, GadgetCallParser;
13201
- var init_parser = __esm({
13923
+ var init_parser2 = __esm({
13202
13924
  "src/gadgets/parser.ts"() {
13203
13925
  "use strict";
13204
13926
  init_constants();
@@ -13404,7 +14126,7 @@ var init_typed_gadget = __esm({
13404
14126
 
13405
14127
  // src/gadgets/executor.ts
13406
14128
  import equal from "fast-deep-equal";
13407
- import { z as z4 } from "zod";
14129
+ import { z as z5 } from "zod";
13408
14130
  function getHostExportsInternal() {
13409
14131
  return {
13410
14132
  AgentBuilder,
@@ -13412,7 +14134,7 @@ function getHostExportsInternal() {
13412
14134
  createGadget,
13413
14135
  ExecutionTree,
13414
14136
  LLMist,
13415
- z: z4
14137
+ z: z5
13416
14138
  };
13417
14139
  }
13418
14140
  var GadgetExecutor;
@@ -13430,7 +14152,7 @@ var init_executor = __esm({
13430
14152
  init_create_gadget();
13431
14153
  init_error_formatter();
13432
14154
  init_exceptions();
13433
- init_parser();
14155
+ init_parser2();
13434
14156
  init_typed_gadget();
13435
14157
  GadgetExecutor = class {
13436
14158
  registry;
@@ -15031,7 +15753,7 @@ var init_stream_processor = __esm({
15031
15753
  "src/agent/stream-processor.ts"() {
15032
15754
  "use strict";
15033
15755
  init_executor();
15034
- init_parser();
15756
+ init_parser2();
15035
15757
  init_logger();
15036
15758
  init_gadget_concurrency_manager();
15037
15759
  init_gadget_dependency_resolver();
@@ -16155,7 +16877,7 @@ init_builder();
16155
16877
  init_event_handlers();
16156
16878
  init_file_logging();
16157
16879
  init_hook_presets();
16158
- import { z as z5 } from "zod";
16880
+ import { z as z6 } from "zod";
16159
16881
 
16160
16882
  // src/agent/compaction/index.ts
16161
16883
  init_config();
@@ -16464,7 +17186,7 @@ function resultWithFile(result, fileData, mimeType, options) {
16464
17186
 
16465
17187
  // src/index.ts
16466
17188
  init_output_viewer();
16467
- init_parser();
17189
+ init_parser2();
16468
17190
  init_registry();
16469
17191
  init_typed_gadget();
16470
17192
  init_constants2();
@@ -16731,6 +17453,14 @@ var SimpleSessionManager = class extends BaseSessionManager {
16731
17453
  }
16732
17454
  };
16733
17455
 
17456
+ // src/skills/index.ts
17457
+ init_activation();
17458
+ init_loader();
17459
+ init_parser();
17460
+ init_registry2();
17461
+ init_skill();
17462
+ init_use_skill_gadget();
17463
+
16734
17464
  // src/utils/format.ts
16735
17465
  function truncate(text3, maxLength, suffix = "...") {
16736
17466
  if (text3.length <= maxLength) return text3;
@@ -16926,11 +17656,14 @@ export {
16926
17656
  OpenRouterProvider,
16927
17657
  RateLimitTracker,
16928
17658
  SimpleSessionManager,
17659
+ Skill,
17660
+ SkillRegistry,
16929
17661
  SlidingWindowStrategy,
16930
17662
  StreamProcessor,
16931
17663
  SummarizationStrategy,
16932
17664
  TaskCompletionSignal,
16933
17665
  TimeoutException,
17666
+ USE_SKILL_GADGET_NAME,
16934
17667
  audioFromBase64,
16935
17668
  audioFromBuffer,
16936
17669
  collectEvents,
@@ -16948,10 +17681,12 @@ export {
16948
17681
  createOpenAIProviderFromEnv,
16949
17682
  createOpenRouterProviderFromEnv,
16950
17683
  createSubagent,
17684
+ createUseSkillGadget,
16951
17685
  defaultLogger,
16952
17686
  detectAudioMimeType,
16953
17687
  detectImageMimeType,
16954
17688
  discoverProviderAdapters,
17689
+ discoverSkills,
16955
17690
  extractMessageText,
16956
17691
  extractRetryAfterMs,
16957
17692
  filterByDepth,
@@ -16994,15 +17729,21 @@ export {
16994
17729
  iterationProgressHint,
16995
17730
  listPresets,
16996
17731
  listSubagents,
17732
+ loadSkillsFromDirectory,
16997
17733
  normalizeMessageContent,
16998
17734
  parallelGadgetHint,
16999
17735
  parseDataUrl,
17736
+ parseFrontmatter,
17000
17737
  parseManifest,
17738
+ parseMetadata,
17001
17739
  parseRetryAfterHeader,
17740
+ parseSkillContent,
17741
+ parseSkillFile,
17002
17742
  randomDelay,
17003
17743
  resetFileLoggingState,
17004
17744
  resolveConfig,
17005
17745
  resolveHintTemplate,
17746
+ resolveInstructions,
17006
17747
  resolveModel,
17007
17748
  resolvePromptTemplate,
17008
17749
  resolveRateLimitConfig,
@@ -17017,9 +17758,12 @@ export {
17017
17758
  resultWithImages,
17018
17759
  resultWithMedia,
17019
17760
  runWithHandlers,
17761
+ scanResources,
17020
17762
  schemaToJSONSchema,
17021
17763
  stream,
17022
17764
  stripProviderPrefix,
17765
+ substituteArguments,
17766
+ substituteVariables,
17023
17767
  text,
17024
17768
  timing,
17025
17769
  toBase64,
@@ -17027,9 +17771,10 @@ export {
17027
17771
  validateAndApplyDefaults,
17028
17772
  validateGadgetParams,
17029
17773
  validateGadgetSchema,
17774
+ validateMetadata,
17030
17775
  withErrorHandling,
17031
17776
  withRetry,
17032
17777
  withTimeout,
17033
- z5 as z
17778
+ z6 as z
17034
17779
  };
17035
17780
  //# sourceMappingURL=index.js.map