llmist 16.1.0 → 16.2.1

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.cjs CHANGED
@@ -358,15 +358,15 @@ var init_execution_tree = __esm({
358
358
  const parentId = params.parentId ?? this.parentNodeId;
359
359
  const parent = parentId ? this.nodes.get(parentId) : null;
360
360
  const depth = parent ? parent.depth + 1 : this.baseDepth;
361
- const path = parent ? [...parent.path] : [];
361
+ const path3 = parent ? [...parent.path] : [];
362
362
  const id = this.generateLLMCallId(params.iteration, parentId);
363
- path.push(id);
363
+ path3.push(id);
364
364
  const node = {
365
365
  id,
366
366
  type: "llm_call",
367
367
  parentId,
368
368
  depth,
369
- path,
369
+ path: path3,
370
370
  createdAt: Date.now(),
371
371
  completedAt: null,
372
372
  iteration: params.iteration,
@@ -477,15 +477,15 @@ var init_execution_tree = __esm({
477
477
  const parentId = params.parentId ?? this.getCurrentLLMCallId() ?? this.parentNodeId;
478
478
  const parent = parentId ? this.nodes.get(parentId) : null;
479
479
  const depth = parent ? parent.depth + 1 : this.baseDepth;
480
- const path = parent ? [...parent.path] : [];
480
+ const path3 = parent ? [...parent.path] : [];
481
481
  const id = this.generateGadgetId(params.invocationId);
482
- path.push(id);
482
+ path3.push(id);
483
483
  const node = {
484
484
  id,
485
485
  type: "gadget",
486
486
  parentId,
487
487
  depth,
488
- path,
488
+ path: path3,
489
489
  createdAt: Date.now(),
490
490
  completedAt: null,
491
491
  invocationId: params.invocationId,
@@ -1424,8 +1424,8 @@ ${this.endPrefix}`
1424
1424
  });
1425
1425
  if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
1426
1426
  const idRefs = media.map((m, i) => {
1427
- const path = storedMedia?.[i]?.path;
1428
- const pathInfo = path ? ` \u2192 saved to: ${path}` : "";
1427
+ const path3 = storedMedia?.[i]?.path;
1428
+ const pathInfo = path3 ? ` \u2192 saved to: ${path3}` : "";
1429
1429
  return `[Media: ${mediaIds[i]} (${m.kind})${pathInfo}]`;
1430
1430
  }).join("\n");
1431
1431
  const textWithIds = `Result (${invocationId}): ${result}
@@ -4032,29 +4032,29 @@ function schemaToJSONSchema(schema, options) {
4032
4032
  }
4033
4033
  function detectDescriptionMismatch(schema, jsonSchema) {
4034
4034
  const mismatches = [];
4035
- function checkSchema(zodSchema, json, path) {
4035
+ function checkSchema(zodSchema, json, path3) {
4036
4036
  if (!zodSchema || typeof zodSchema !== "object") return;
4037
4037
  const def = zodSchema._def;
4038
4038
  const jsonObj = json;
4039
4039
  if (def?.description && !jsonObj?.description) {
4040
- mismatches.push(path || "root");
4040
+ mismatches.push(path3 || "root");
4041
4041
  }
4042
4042
  if (def?.typeName === "ZodObject" && def?.shape) {
4043
4043
  const shape = typeof def.shape === "function" ? def.shape() : def.shape;
4044
4044
  for (const [key, fieldSchema] of Object.entries(shape)) {
4045
4045
  const properties = jsonObj?.properties;
4046
4046
  const jsonProp = properties?.[key];
4047
- checkSchema(fieldSchema, jsonProp, path ? `${path}.${key}` : key);
4047
+ checkSchema(fieldSchema, jsonProp, path3 ? `${path3}.${key}` : key);
4048
4048
  }
4049
4049
  }
4050
4050
  if (def?.typeName === "ZodArray" && def?.type) {
4051
- checkSchema(def.type, jsonObj?.items, path ? `${path}[]` : "[]");
4051
+ checkSchema(def.type, jsonObj?.items, path3 ? `${path3}[]` : "[]");
4052
4052
  }
4053
4053
  if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
4054
- checkSchema(def.innerType, json, path);
4054
+ checkSchema(def.innerType, json, path3);
4055
4055
  }
4056
4056
  if (def?.typeName === "ZodDefault" && def?.innerType) {
4057
- checkSchema(def.innerType, json, path);
4057
+ checkSchema(def.innerType, json, path3);
4058
4058
  }
4059
4059
  }
4060
4060
  checkSchema(schema, jsonSchema, "");
@@ -4147,7 +4147,7 @@ Example fixes:
4147
4147
  );
4148
4148
  }
4149
4149
  }
4150
- function findUnknownTypes(schema, path = []) {
4150
+ function findUnknownTypes(schema, path3 = []) {
4151
4151
  const issues = [];
4152
4152
  if (!schema || typeof schema !== "object") {
4153
4153
  return issues;
@@ -4159,7 +4159,7 @@ function findUnknownTypes(schema, path = []) {
4159
4159
  }
4160
4160
  if (schema.properties) {
4161
4161
  for (const [propName, propSchema] of Object.entries(schema.properties)) {
4162
- const propPath = [...path, propName];
4162
+ const propPath = [...path3, propName];
4163
4163
  if (hasNoType(propSchema)) {
4164
4164
  issues.push(propPath.join(".") || propName);
4165
4165
  }
@@ -4167,7 +4167,7 @@ function findUnknownTypes(schema, path = []) {
4167
4167
  }
4168
4168
  }
4169
4169
  if (schema.items) {
4170
- const itemPath = [...path, "[]"];
4170
+ const itemPath = [...path3, "[]"];
4171
4171
  if (hasNoType(schema.items)) {
4172
4172
  issues.push(itemPath.join("."));
4173
4173
  }
@@ -4175,17 +4175,17 @@ function findUnknownTypes(schema, path = []) {
4175
4175
  }
4176
4176
  if (schema.anyOf) {
4177
4177
  schema.anyOf.forEach((subSchema, index) => {
4178
- issues.push(...findUnknownTypes(subSchema, [...path, `anyOf[${index}]`]));
4178
+ issues.push(...findUnknownTypes(subSchema, [...path3, `anyOf[${index}]`]));
4179
4179
  });
4180
4180
  }
4181
4181
  if (schema.oneOf) {
4182
4182
  schema.oneOf.forEach((subSchema, index) => {
4183
- issues.push(...findUnknownTypes(subSchema, [...path, `oneOf[${index}]`]));
4183
+ issues.push(...findUnknownTypes(subSchema, [...path3, `oneOf[${index}]`]));
4184
4184
  });
4185
4185
  }
4186
4186
  if (schema.allOf) {
4187
4187
  schema.allOf.forEach((subSchema, index) => {
4188
- issues.push(...findUnknownTypes(subSchema, [...path, `allOf[${index}]`]));
4188
+ issues.push(...findUnknownTypes(subSchema, [...path3, `allOf[${index}]`]));
4189
4189
  });
4190
4190
  }
4191
4191
  return issues;
@@ -5203,6 +5203,626 @@ var init_registry = __esm({
5203
5203
  }
5204
5204
  });
5205
5205
 
5206
+ // src/skills/activation.ts
5207
+ function substituteArguments(instructions, args) {
5208
+ if (!args) {
5209
+ return instructions.replace(/\$ARGUMENTS\[\d+\]/g, "").replace(/\$ARGUMENTS/g, "").replace(/\$\d+/g, "");
5210
+ }
5211
+ const parts = splitArguments(args);
5212
+ let result = instructions.replace(/\$ARGUMENTS\[(\d+)\]/g, (_match, index) => {
5213
+ const i = Number.parseInt(index, 10);
5214
+ return parts[i] ?? "";
5215
+ });
5216
+ result = result.replace(/\$ARGUMENTS/g, args);
5217
+ result = result.replace(/\$(\d+)/g, (_match, index) => {
5218
+ const i = Number.parseInt(index, 10);
5219
+ return parts[i] ?? "";
5220
+ });
5221
+ return result;
5222
+ }
5223
+ function preprocessShellCommands(instructions, options) {
5224
+ const { cwd, shell = "bash", timeoutMs = 1e4 } = options ?? {};
5225
+ return instructions.replace(/!`([^`]+)`/g, (_match, command) => {
5226
+ try {
5227
+ const output = (0, import_node_child_process.execSync)(command, {
5228
+ cwd,
5229
+ shell: shell === "powershell" ? "powershell" : "/bin/bash",
5230
+ timeout: timeoutMs,
5231
+ encoding: "utf-8",
5232
+ stdio: ["pipe", "pipe", "pipe"]
5233
+ });
5234
+ return output.trim();
5235
+ } catch (error) {
5236
+ const msg = error instanceof Error ? error.message : String(error);
5237
+ return `[Error executing \`${command}\`: ${msg}]`;
5238
+ }
5239
+ });
5240
+ }
5241
+ function substituteVariables(instructions, variables) {
5242
+ return instructions.replace(/\$\{(\w+)\}/g, (_match, varName) => {
5243
+ return variables[varName] ?? "";
5244
+ });
5245
+ }
5246
+ function resolveInstructions(instructions, options) {
5247
+ let resolved = instructions;
5248
+ if (options?.variables) {
5249
+ resolved = substituteVariables(resolved, options.variables);
5250
+ }
5251
+ resolved = substituteArguments(resolved, options?.arguments);
5252
+ if (options?.enableShellPreprocessing !== false) {
5253
+ resolved = preprocessShellCommands(resolved, {
5254
+ cwd: options?.cwd,
5255
+ shell: options?.shell,
5256
+ timeoutMs: options?.shellTimeoutMs
5257
+ });
5258
+ }
5259
+ return resolved;
5260
+ }
5261
+ function splitArguments(args) {
5262
+ const parts = [];
5263
+ let current = "";
5264
+ let inQuote = null;
5265
+ for (const char of args) {
5266
+ if (inQuote) {
5267
+ if (char === inQuote) {
5268
+ inQuote = null;
5269
+ } else {
5270
+ current += char;
5271
+ }
5272
+ } else if (char === '"' || char === "'") {
5273
+ inQuote = char;
5274
+ } else if (char === " " || char === " ") {
5275
+ if (current) {
5276
+ parts.push(current);
5277
+ current = "";
5278
+ }
5279
+ } else {
5280
+ current += char;
5281
+ }
5282
+ }
5283
+ if (current) parts.push(current);
5284
+ return parts;
5285
+ }
5286
+ var import_node_child_process;
5287
+ var init_activation = __esm({
5288
+ "src/skills/activation.ts"() {
5289
+ "use strict";
5290
+ import_node_child_process = require("child_process");
5291
+ }
5292
+ });
5293
+
5294
+ // src/skills/parser.ts
5295
+ function parseFrontmatter(content) {
5296
+ const trimmed = content.trimStart();
5297
+ if (!trimmed.startsWith("---")) {
5298
+ return { frontmatter: {}, body: content };
5299
+ }
5300
+ const endIndex = trimmed.indexOf("\n---", 3);
5301
+ if (endIndex === -1) {
5302
+ return { frontmatter: {}, body: content };
5303
+ }
5304
+ const yamlBlock = trimmed.slice(3, endIndex).trim();
5305
+ const body = trimmed.slice(endIndex + 4).trim();
5306
+ const parsed = import_js_yaml.default.load(yamlBlock);
5307
+ const frontmatter = typeof parsed === "object" && parsed !== null ? parsed : {};
5308
+ return { frontmatter, body };
5309
+ }
5310
+ function parseMetadata(frontmatter, fallbackName) {
5311
+ const name = parseString(frontmatter.name) ?? fallbackName ?? "unnamed-skill";
5312
+ const description = parseString(frontmatter.description) ?? "";
5313
+ return {
5314
+ name,
5315
+ description,
5316
+ argumentHint: parseString(frontmatter["argument-hint"]),
5317
+ allowedTools: parseStringArray(frontmatter["allowed-tools"]),
5318
+ model: parseString(frontmatter.model),
5319
+ context: parseContext(frontmatter.context),
5320
+ agent: parseString(frontmatter.agent),
5321
+ paths: parseStringArray(frontmatter.paths),
5322
+ gadgets: parseStringArray(frontmatter.gadgets),
5323
+ disableModelInvocation: parseBool(frontmatter["disable-model-invocation"]),
5324
+ userInvocable: parseBool(frontmatter["user-invocable"]),
5325
+ shell: parseShell(frontmatter.shell),
5326
+ version: parseString(frontmatter.version)
5327
+ };
5328
+ }
5329
+ function scanResources(skillDir) {
5330
+ const resources = [];
5331
+ for (const category of RESOURCE_CATEGORIES) {
5332
+ const categoryDir = import_node_path3.default.join(skillDir, category);
5333
+ if (!import_node_fs2.default.existsSync(categoryDir)) continue;
5334
+ const stat = import_node_fs2.default.statSync(categoryDir);
5335
+ if (!stat.isDirectory()) continue;
5336
+ for (const file of walkDirectory(categoryDir)) {
5337
+ resources.push({
5338
+ relativePath: import_node_path3.default.relative(skillDir, file),
5339
+ absolutePath: file,
5340
+ category
5341
+ });
5342
+ }
5343
+ }
5344
+ return resources;
5345
+ }
5346
+ function parseSkillFile(skillMdPath, source, loadInstructions = false) {
5347
+ const content = import_node_fs2.default.readFileSync(skillMdPath, "utf-8");
5348
+ return parseSkillContent(content, skillMdPath, source, loadInstructions);
5349
+ }
5350
+ function parseSkillContent(content, sourcePath, source, loadInstructions = false) {
5351
+ const sourceDir = import_node_path3.default.dirname(sourcePath);
5352
+ const fallbackName = import_node_path3.default.basename(sourceDir);
5353
+ const { frontmatter, body } = parseFrontmatter(content);
5354
+ const metadata = parseMetadata(frontmatter, fallbackName);
5355
+ const resources = import_node_fs2.default.existsSync(sourceDir) ? scanResources(sourceDir) : [];
5356
+ return {
5357
+ metadata,
5358
+ instructions: loadInstructions ? body : null,
5359
+ resources,
5360
+ sourcePath,
5361
+ sourceDir,
5362
+ source
5363
+ };
5364
+ }
5365
+ function validateMetadata(metadata) {
5366
+ const issues = [];
5367
+ if (!metadata.name) {
5368
+ issues.push("Skill name is required");
5369
+ } else {
5370
+ if (metadata.name.length > MAX_NAME_LENGTH) {
5371
+ issues.push(`Skill name exceeds ${MAX_NAME_LENGTH} characters`);
5372
+ }
5373
+ if (!NAME_PATTERN.test(metadata.name)) {
5374
+ issues.push(
5375
+ "Skill name must contain only lowercase letters, numbers, and hyphens, and must start with a letter or number"
5376
+ );
5377
+ }
5378
+ }
5379
+ if (!metadata.description) {
5380
+ issues.push("Skill description is required");
5381
+ } else if (metadata.description.length > MAX_DESCRIPTION_LENGTH) {
5382
+ issues.push(`Skill description exceeds ${MAX_DESCRIPTION_LENGTH} characters`);
5383
+ }
5384
+ if (metadata.context && metadata.context !== "fork" && metadata.context !== "inline") {
5385
+ issues.push('Skill context must be "fork" or "inline"');
5386
+ }
5387
+ return issues;
5388
+ }
5389
+ function parseString(value) {
5390
+ if (value == null) return void 0;
5391
+ if (typeof value === "string") return value;
5392
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
5393
+ return void 0;
5394
+ }
5395
+ function parseStringArray(value) {
5396
+ if (value == null) return void 0;
5397
+ if (Array.isArray(value)) {
5398
+ return value.filter((v) => typeof v === "string" || typeof v === "number").map(String);
5399
+ }
5400
+ if (typeof value === "string") return [value];
5401
+ return void 0;
5402
+ }
5403
+ function parseContext(value) {
5404
+ if (value === "fork" || value === "inline") return value;
5405
+ return void 0;
5406
+ }
5407
+ function parseShell(value) {
5408
+ if (value === "bash" || value === "powershell") return value;
5409
+ return void 0;
5410
+ }
5411
+ function parseBool(value) {
5412
+ if (value === true || value === false) return value;
5413
+ if (value === "true") return true;
5414
+ if (value === "false") return false;
5415
+ return void 0;
5416
+ }
5417
+ function* walkDirectory(dir) {
5418
+ const entries = import_node_fs2.default.readdirSync(dir, { withFileTypes: true });
5419
+ for (const entry of entries) {
5420
+ const fullPath = import_node_path3.default.join(dir, entry.name);
5421
+ if (entry.isDirectory()) {
5422
+ yield* walkDirectory(fullPath);
5423
+ } else if (entry.isFile()) {
5424
+ yield fullPath;
5425
+ }
5426
+ }
5427
+ }
5428
+ var import_node_fs2, import_node_path3, import_js_yaml, RESOURCE_CATEGORIES, MAX_NAME_LENGTH, MAX_DESCRIPTION_LENGTH, NAME_PATTERN;
5429
+ var init_parser = __esm({
5430
+ "src/skills/parser.ts"() {
5431
+ "use strict";
5432
+ import_node_fs2 = __toESM(require("fs"), 1);
5433
+ import_node_path3 = __toESM(require("path"), 1);
5434
+ import_js_yaml = __toESM(require("js-yaml"), 1);
5435
+ RESOURCE_CATEGORIES = ["scripts", "references", "assets"];
5436
+ MAX_NAME_LENGTH = 64;
5437
+ MAX_DESCRIPTION_LENGTH = 1024;
5438
+ NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
5439
+ }
5440
+ });
5441
+
5442
+ // src/skills/registry.ts
5443
+ var registry_exports = {};
5444
+ __export(registry_exports, {
5445
+ SkillRegistry: () => SkillRegistry
5446
+ });
5447
+ var import_minimatch, DEFAULT_CHAR_BUDGET, SUMMARY_DESCRIPTION_LIMIT, SkillRegistry;
5448
+ var init_registry2 = __esm({
5449
+ "src/skills/registry.ts"() {
5450
+ "use strict";
5451
+ import_minimatch = require("minimatch");
5452
+ DEFAULT_CHAR_BUDGET = 8e3;
5453
+ SUMMARY_DESCRIPTION_LIMIT = 250;
5454
+ SkillRegistry = class _SkillRegistry {
5455
+ skills = /* @__PURE__ */ new Map();
5456
+ /**
5457
+ * Register a skill. Overwrites any existing skill with the same name.
5458
+ *
5459
+ * Unlike GadgetRegistry (which throws on duplicates), SkillRegistry allows
5460
+ * overwriting because skills are loaded from multiple sources with intentional
5461
+ * priority ordering (project > user > default).
5462
+ */
5463
+ register(skill) {
5464
+ this.skills.set(skill.name.toLowerCase(), skill);
5465
+ }
5466
+ /** Register multiple skills. */
5467
+ registerMany(skills) {
5468
+ for (const skill of skills) {
5469
+ this.register(skill);
5470
+ }
5471
+ }
5472
+ /** Remove a skill by name (case-insensitive). Returns true if removed. */
5473
+ remove(name) {
5474
+ return this.skills.delete(name.toLowerCase());
5475
+ }
5476
+ /** Remove all registered skills. */
5477
+ clear() {
5478
+ this.skills.clear();
5479
+ }
5480
+ /** Get a skill by name (case-insensitive). */
5481
+ get(name) {
5482
+ return this.skills.get(name.toLowerCase());
5483
+ }
5484
+ /** Check if a skill exists by name (case-insensitive). */
5485
+ has(name) {
5486
+ return this.skills.has(name.toLowerCase());
5487
+ }
5488
+ /** Get all registered skills. */
5489
+ getAll() {
5490
+ return [...this.skills.values()];
5491
+ }
5492
+ /** Get all skill names. */
5493
+ getNames() {
5494
+ return [...this.skills.keys()];
5495
+ }
5496
+ /** Number of registered skills. */
5497
+ get size() {
5498
+ return this.skills.size;
5499
+ }
5500
+ /**
5501
+ * Get skills that are visible to the LLM for auto-triggering.
5502
+ * Excludes skills with disableModelInvocation: true.
5503
+ */
5504
+ getModelInvocable() {
5505
+ return this.getAll().filter((s) => s.isModelInvocable);
5506
+ }
5507
+ /**
5508
+ * Get skills that the user can invoke via /skill-name.
5509
+ * Excludes skills with userInvocable: false.
5510
+ */
5511
+ getUserInvocable() {
5512
+ return this.getAll().filter((s) => s.isUserInvocable);
5513
+ }
5514
+ /**
5515
+ * Generate metadata summaries for system prompt injection (Tier 1).
5516
+ *
5517
+ * Each skill contributes a one-line summary: "name — description".
5518
+ * Output is truncated to fit the character budget.
5519
+ *
5520
+ * @param charBudget - Maximum characters for all summaries combined.
5521
+ */
5522
+ getMetadataSummaries(charBudget = DEFAULT_CHAR_BUDGET) {
5523
+ const invocable = this.getModelInvocable();
5524
+ if (invocable.length === 0) return "";
5525
+ const lines = [];
5526
+ let totalChars = 0;
5527
+ for (const skill of invocable) {
5528
+ const desc = skill.description.length > SUMMARY_DESCRIPTION_LIMIT ? `${skill.description.slice(0, SUMMARY_DESCRIPTION_LIMIT - 3)}...` : skill.description;
5529
+ const line = `- ${skill.name}: ${desc}`;
5530
+ if (totalChars + line.length > charBudget) break;
5531
+ lines.push(line);
5532
+ totalChars += line.length + 1;
5533
+ }
5534
+ return lines.join("\n");
5535
+ }
5536
+ /**
5537
+ * Find skills whose `paths` patterns match a given file path.
5538
+ * Used for auto-activation when the user is working on specific files.
5539
+ */
5540
+ findByFilePath(filePath) {
5541
+ return this.getModelInvocable().filter((skill) => {
5542
+ const patterns = skill.metadata.paths;
5543
+ if (!patterns || patterns.length === 0) return false;
5544
+ return patterns.some((pattern) => (0, import_minimatch.minimatch)(filePath, pattern));
5545
+ });
5546
+ }
5547
+ /**
5548
+ * Merge another registry into this one.
5549
+ * Skills from the other registry overwrite existing skills with the same name.
5550
+ */
5551
+ merge(other) {
5552
+ for (const skill of other.getAll()) {
5553
+ this.register(skill);
5554
+ }
5555
+ }
5556
+ /** Create a registry from an array of skills. */
5557
+ static from(skills) {
5558
+ const registry = new _SkillRegistry();
5559
+ registry.registerMany(skills);
5560
+ return registry;
5561
+ }
5562
+ };
5563
+ }
5564
+ });
5565
+
5566
+ // src/skills/skill.ts
5567
+ var import_promises2, Skill;
5568
+ var init_skill = __esm({
5569
+ "src/skills/skill.ts"() {
5570
+ "use strict";
5571
+ import_promises2 = __toESM(require("fs/promises"), 1);
5572
+ init_activation();
5573
+ init_parser();
5574
+ Skill = class _Skill {
5575
+ metadata;
5576
+ sourcePath;
5577
+ sourceDir;
5578
+ source;
5579
+ _instructions;
5580
+ _resources;
5581
+ _resourceCache = /* @__PURE__ */ new Map();
5582
+ _resourceLoading = /* @__PURE__ */ new Map();
5583
+ constructor(parsed) {
5584
+ this.metadata = parsed.metadata;
5585
+ this.sourcePath = parsed.sourcePath;
5586
+ this.sourceDir = parsed.sourceDir;
5587
+ this.source = parsed.source;
5588
+ this._instructions = parsed.instructions;
5589
+ this._resources = parsed.resources;
5590
+ }
5591
+ /** Skill name for registry lookup. */
5592
+ get name() {
5593
+ return this.metadata.name;
5594
+ }
5595
+ /** Skill description for LLM matching. */
5596
+ get description() {
5597
+ return this.metadata.description;
5598
+ }
5599
+ /** Whether the LLM can auto-trigger this skill. */
5600
+ get isModelInvocable() {
5601
+ return this.metadata.disableModelInvocation !== true;
5602
+ }
5603
+ /** Whether the user can invoke this skill via /skill-name. */
5604
+ get isUserInvocable() {
5605
+ return this.metadata.userInvocable !== false;
5606
+ }
5607
+ /**
5608
+ * Load and cache Tier 2 instructions.
5609
+ * If instructions were loaded during parsing, returns the cached value.
5610
+ */
5611
+ async getInstructions() {
5612
+ if (this._instructions !== null) return this._instructions;
5613
+ const content = await import_promises2.default.readFile(this.sourcePath, "utf-8");
5614
+ const { body } = parseFrontmatter(content);
5615
+ this._instructions = body;
5616
+ return body;
5617
+ }
5618
+ /**
5619
+ * List Tier 3 resources.
5620
+ * Resources are discovered at parse time but content is loaded on demand.
5621
+ */
5622
+ getResources() {
5623
+ return this._resources;
5624
+ }
5625
+ /**
5626
+ * Load a specific Tier 3 resource by relative path.
5627
+ * Results are cached for the lifetime of this Skill instance.
5628
+ * Concurrent calls for the same resource share a single read.
5629
+ */
5630
+ async getResource(relativePath) {
5631
+ if (relativePath.includes("..")) {
5632
+ throw new Error(`Invalid resource path (path traversal): ${relativePath}`);
5633
+ }
5634
+ const cached = this._resourceCache.get(relativePath);
5635
+ if (cached !== void 0) return cached;
5636
+ const existing = this._resourceLoading.get(relativePath);
5637
+ if (existing) return existing;
5638
+ const resource = this._resources.find((r) => r.relativePath === relativePath);
5639
+ if (!resource) {
5640
+ throw new Error(`Resource not found: ${relativePath} in skill ${this.name}`);
5641
+ }
5642
+ const loadPromise = import_promises2.default.readFile(resource.absolutePath, "utf-8").then(
5643
+ (content) => {
5644
+ this._resourceCache.set(relativePath, content);
5645
+ this._resourceLoading.delete(relativePath);
5646
+ return content;
5647
+ },
5648
+ (error) => {
5649
+ this._resourceLoading.delete(relativePath);
5650
+ throw new Error(
5651
+ `Failed to load resource ${relativePath} in skill ${this.name}: ${error instanceof Error ? error.message : String(error)}`
5652
+ );
5653
+ }
5654
+ );
5655
+ this._resourceLoading.set(relativePath, loadPromise);
5656
+ return loadPromise;
5657
+ }
5658
+ /**
5659
+ * Activate this skill with optional arguments.
5660
+ *
5661
+ * Performs:
5662
+ * 1. Variable substitution (${SKILL_DIR}, etc.)
5663
+ * 2. Argument substitution ($ARGUMENTS, $0, $1)
5664
+ * 3. Shell preprocessing (!`command`)
5665
+ * 4. Resource loading (if eagerResources is true)
5666
+ */
5667
+ async activate(options) {
5668
+ const instructions = await this.getInstructions();
5669
+ const resolvedInstructions = resolveInstructions(instructions, {
5670
+ arguments: options?.arguments,
5671
+ variables: {
5672
+ SKILL_DIR: this.sourceDir,
5673
+ CLAUDE_SKILL_DIR: this.sourceDir
5674
+ },
5675
+ cwd: options?.cwd ?? this.sourceDir,
5676
+ shell: this.metadata.shell,
5677
+ enableShellPreprocessing: options?.enableShellPreprocessing,
5678
+ shellTimeoutMs: options?.shellTimeoutMs
5679
+ });
5680
+ const loadedResources = /* @__PURE__ */ new Map();
5681
+ if (options?.eagerResources) {
5682
+ for (const resource of this._resources) {
5683
+ const content = await this.getResource(resource.relativePath);
5684
+ loadedResources.set(resource.relativePath, content);
5685
+ }
5686
+ }
5687
+ return {
5688
+ skillName: this.name,
5689
+ resolvedInstructions,
5690
+ gadgets: [],
5691
+ // Gadgets are resolved by the CLI layer
5692
+ loadedResources
5693
+ };
5694
+ }
5695
+ /**
5696
+ * Create a Skill from a SKILL.md content string.
5697
+ * Useful for testing or dynamic skill creation.
5698
+ */
5699
+ static fromContent(content, sourcePath, source = { type: "directory", path: sourcePath }) {
5700
+ const parsed = parseSkillContent(content, sourcePath, source, true);
5701
+ return new _Skill(parsed);
5702
+ }
5703
+ };
5704
+ }
5705
+ });
5706
+
5707
+ // src/skills/loader.ts
5708
+ function loadSkillsFromDirectory(dir, source, onWarning) {
5709
+ if (!import_node_fs3.default.existsSync(dir)) return [];
5710
+ const stat = import_node_fs3.default.statSync(dir);
5711
+ if (!stat.isDirectory()) return [];
5712
+ const skills = [];
5713
+ scanForSkills(dir, source, skills, onWarning);
5714
+ return skills;
5715
+ }
5716
+ function discoverSkills(options) {
5717
+ const registry = new SkillRegistry();
5718
+ const userSkillsDir = options?.userDir ?? import_node_path4.default.join(import_node_os2.default.homedir(), CONFIG_DIR_NAME, SKILLS_DIR_NAME);
5719
+ const userSkills = loadSkillsFromDirectory(userSkillsDir, {
5720
+ type: "user",
5721
+ path: userSkillsDir
5722
+ });
5723
+ registry.registerMany(userSkills);
5724
+ if (options?.projectDir) {
5725
+ const projectSkillsDir = import_node_path4.default.join(options.projectDir, CONFIG_DIR_NAME, SKILLS_DIR_NAME);
5726
+ const projectSkills = loadSkillsFromDirectory(projectSkillsDir, {
5727
+ type: "project",
5728
+ path: projectSkillsDir
5729
+ });
5730
+ registry.registerMany(projectSkills);
5731
+ }
5732
+ if (options?.additionalDirs) {
5733
+ for (const dir of options.additionalDirs) {
5734
+ const resolvedDir = dir.startsWith("~") ? import_node_path4.default.join(import_node_os2.default.homedir(), dir.slice(1)) : dir;
5735
+ const skills = loadSkillsFromDirectory(resolvedDir, {
5736
+ type: "directory",
5737
+ path: resolvedDir
5738
+ });
5739
+ registry.registerMany(skills);
5740
+ }
5741
+ }
5742
+ return registry;
5743
+ }
5744
+ function scanForSkills(dir, source, results, onWarning) {
5745
+ let entries;
5746
+ try {
5747
+ entries = import_node_fs3.default.readdirSync(dir, { withFileTypes: true });
5748
+ } catch (error) {
5749
+ onWarning?.(
5750
+ `Cannot read skill directory ${dir}: ${error instanceof Error ? error.message : String(error)}`
5751
+ );
5752
+ return;
5753
+ }
5754
+ const skillMdPath = import_node_path4.default.join(dir, "SKILL.md");
5755
+ if (import_node_fs3.default.existsSync(skillMdPath)) {
5756
+ try {
5757
+ const parsed = parseSkillFile(skillMdPath, source, false);
5758
+ results.push(new Skill(parsed));
5759
+ } catch (error) {
5760
+ onWarning?.(
5761
+ `Failed to parse ${skillMdPath}: ${error instanceof Error ? error.message : String(error)}`
5762
+ );
5763
+ }
5764
+ return;
5765
+ }
5766
+ for (const entry of entries) {
5767
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
5768
+ scanForSkills(import_node_path4.default.join(dir, entry.name), source, results, onWarning);
5769
+ }
5770
+ }
5771
+ }
5772
+ var import_node_fs3, import_node_os2, import_node_path4, SKILLS_DIR_NAME, CONFIG_DIR_NAME;
5773
+ var init_loader = __esm({
5774
+ "src/skills/loader.ts"() {
5775
+ "use strict";
5776
+ import_node_fs3 = __toESM(require("fs"), 1);
5777
+ import_node_os2 = __toESM(require("os"), 1);
5778
+ import_node_path4 = __toESM(require("path"), 1);
5779
+ init_parser();
5780
+ init_registry2();
5781
+ init_skill();
5782
+ SKILLS_DIR_NAME = "skills";
5783
+ CONFIG_DIR_NAME = ".llmist";
5784
+ }
5785
+ });
5786
+
5787
+ // src/skills/use-skill-gadget.ts
5788
+ function createUseSkillGadget(registry) {
5789
+ const summaries = registry.getMetadataSummaries();
5790
+ const skillNames = registry.getModelInvocable().map((s) => s.name);
5791
+ const description = [
5792
+ "Activate a skill to get specialized instructions for a task.",
5793
+ "Available skills:",
5794
+ summaries
5795
+ ].join("\n");
5796
+ return createGadget({
5797
+ name: USE_SKILL_GADGET_NAME,
5798
+ description,
5799
+ schema: import_zod2.z.object({
5800
+ skill: import_zod2.z.enum(skillNames).describe("Name of the skill to activate"),
5801
+ arguments: import_zod2.z.string().optional().describe("Arguments for the skill (e.g., a filename, issue number, or search query)")
5802
+ }),
5803
+ execute: async ({ skill: skillName, arguments: args }) => {
5804
+ const skill = registry.get(skillName);
5805
+ if (!skill) {
5806
+ return `Unknown skill: "${skillName}". Available skills: ${skillNames.join(", ")}`;
5807
+ }
5808
+ const activation = await skill.activate({
5809
+ arguments: args,
5810
+ cwd: process.cwd()
5811
+ });
5812
+ return activation.resolvedInstructions;
5813
+ }
5814
+ });
5815
+ }
5816
+ var import_zod2, USE_SKILL_GADGET_NAME;
5817
+ var init_use_skill_gadget = __esm({
5818
+ "src/skills/use-skill-gadget.ts"() {
5819
+ "use strict";
5820
+ import_zod2 = require("zod");
5821
+ init_create_gadget();
5822
+ USE_SKILL_GADGET_NAME = "UseSkill";
5823
+ }
5824
+ });
5825
+
5206
5826
  // src/agent/builder-utils.ts
5207
5827
  function formatGadgetCall(gadgetName, invocationId, parameters, prefixes) {
5208
5828
  const startPrefix = prefixes?.start ?? GADGET_START_PREFIX;
@@ -5336,7 +5956,7 @@ function resolveLoggingDirectory(state, baseDirectory, counterPadding, subagentC
5336
5956
  if (!fullPath) {
5337
5957
  const chronoNumber = getNextCounter(state, parentDir);
5338
5958
  const subdirName = `${formatCallNumber(chronoNumber, counterPadding)}-${parentGadgetInvocationId}`;
5339
- fullPath = (0, import_node_path3.join)(parentDir, subdirName);
5959
+ fullPath = (0, import_node_path5.join)(parentDir, subdirName);
5340
5960
  state.subagentDirectories.set(subagentKey, fullPath);
5341
5961
  }
5342
5962
  state.activeDirectoryByContext.set(contextKey, fullPath);
@@ -5355,8 +5975,8 @@ function formatCallNumber(n, padding = 4) {
5355
5975
  return n.toString().padStart(padding, "0");
5356
5976
  }
5357
5977
  async function writeLogFile(dir, filename, content) {
5358
- await (0, import_promises2.mkdir)(dir, { recursive: true });
5359
- await (0, import_promises2.writeFile)((0, import_node_path3.join)(dir, filename), content, "utf-8");
5978
+ await (0, import_promises3.mkdir)(dir, { recursive: true });
5979
+ await (0, import_promises3.writeFile)((0, import_node_path5.join)(dir, filename), content, "utf-8");
5360
5980
  }
5361
5981
  function createFileLoggingHooks(options, state = getDefaultState()) {
5362
5982
  const {
@@ -5366,7 +5986,7 @@ function createFileLoggingHooks(options, state = getDefaultState()) {
5366
5986
  formatRequest = formatLlmRequest,
5367
5987
  onFileWritten
5368
5988
  } = options;
5369
- const baseDirectory = (0, import_node_path3.resolve)(options.directory);
5989
+ const baseDirectory = (0, import_node_path5.resolve)(options.directory);
5370
5990
  if (!state.counters.has(baseDirectory)) {
5371
5991
  state.counters.set(baseDirectory, startingCounter - 1);
5372
5992
  }
@@ -5394,7 +6014,7 @@ function createFileLoggingHooks(options, state = getDefaultState()) {
5394
6014
  await writeLogFile(currentDirectory, filename, content);
5395
6015
  if (onFileWritten) {
5396
6016
  onFileWritten({
5397
- filePath: (0, import_node_path3.join)(currentDirectory, filename),
6017
+ filePath: (0, import_node_path5.join)(currentDirectory, filename),
5398
6018
  type: "request",
5399
6019
  callNumber: currentCallNumber,
5400
6020
  contentLength: content.length,
@@ -5423,7 +6043,7 @@ function createFileLoggingHooks(options, state = getDefaultState()) {
5423
6043
  await writeLogFile(currentDirectory, filename, content);
5424
6044
  if (onFileWritten) {
5425
6045
  onFileWritten({
5426
- filePath: (0, import_node_path3.join)(currentDirectory, filename),
6046
+ filePath: (0, import_node_path5.join)(currentDirectory, filename),
5427
6047
  type: "response",
5428
6048
  callNumber: currentCallNumber,
5429
6049
  contentLength: content.length,
@@ -5445,12 +6065,12 @@ function getEnvFileLoggingHooks() {
5445
6065
  }
5446
6066
  return createFileLoggingHooks({ directory });
5447
6067
  }
5448
- var import_promises2, import_node_path3, defaultState, ENV_LOG_RAW_DIRECTORY;
6068
+ var import_promises3, import_node_path5, defaultState, ENV_LOG_RAW_DIRECTORY;
5449
6069
  var init_file_logging = __esm({
5450
6070
  "src/agent/file-logging.ts"() {
5451
6071
  "use strict";
5452
- import_promises2 = require("fs/promises");
5453
- import_node_path3 = require("path");
6072
+ import_promises3 = require("fs/promises");
6073
+ import_node_path5 = require("path");
5454
6074
  init_messages();
5455
6075
  ENV_LOG_RAW_DIRECTORY = "LLMIST_LOG_RAW_DIRECTORY";
5456
6076
  }
@@ -12207,6 +12827,10 @@ var init_builder = __esm({
12207
12827
  "use strict";
12208
12828
  init_model_shortcuts();
12209
12829
  init_registry();
12830
+ init_activation();
12831
+ init_loader();
12832
+ init_parser();
12833
+ init_use_skill_gadget();
12210
12834
  init_agent();
12211
12835
  init_agent_internal_key();
12212
12836
  init_builder_utils();
@@ -12218,12 +12842,14 @@ var init_builder = __esm({
12218
12842
  retry;
12219
12843
  subagents;
12220
12844
  policies;
12845
+ skills;
12221
12846
  constructor(client) {
12222
12847
  this.core = { client, initialMessages: [] };
12223
12848
  this.gadgets = { gadgets: [] };
12224
12849
  this.retry = {};
12225
12850
  this.subagents = {};
12226
12851
  this.policies = {};
12852
+ this.skills = { preActivated: [], skillDirs: [] };
12227
12853
  }
12228
12854
  /** Set the model to use. Supports aliases like "sonnet", "flash". */
12229
12855
  withModel(model) {
@@ -12360,6 +12986,38 @@ var init_builder = __esm({
12360
12986
  this.policies.compactionConfig = { enabled: false };
12361
12987
  return this;
12362
12988
  }
12989
+ // ─── Skills ──────────────────────────────────────────────────────────────────
12990
+ /** Register a skill registry for this agent. */
12991
+ withSkills(registry) {
12992
+ this.skills.registry = registry;
12993
+ return this;
12994
+ }
12995
+ /**
12996
+ * Pre-activate a specific skill before the agent starts.
12997
+ * Instructions are injected into the system prompt.
12998
+ *
12999
+ * Note: each call replaces (not appends) the pre-activated skill for that name.
13000
+ * This is safe for REPL loops where the same builder is reused.
13001
+ */
13002
+ withSkill(name, args) {
13003
+ const existing = this.skills.preActivated.findIndex((s) => s.name === name);
13004
+ if (existing !== -1) {
13005
+ this.skills.preActivated[existing] = { name, args };
13006
+ } else {
13007
+ this.skills.preActivated.push({ name, args });
13008
+ }
13009
+ return this;
13010
+ }
13011
+ /** Clear all pre-activated skills. Call between REPL iterations. */
13012
+ clearPreActivatedSkills() {
13013
+ this.skills.preActivated = [];
13014
+ return this;
13015
+ }
13016
+ /** Add a directory to scan for skills. */
13017
+ withSkillsFrom(dir) {
13018
+ this.skills.skillDirs.push(dir);
13019
+ return this;
13020
+ }
12363
13021
  /** Configure retry behavior for LLM API calls. */
12364
13022
  withRetry(config) {
12365
13023
  this.retry.retryConfig = { ...config, enabled: config.enabled ?? true };
@@ -12451,16 +13109,75 @@ var init_builder = __esm({
12451
13109
  composeHooks() {
12452
13110
  return HookComposer.compose(this.core.hooks, this.core.trailingMessage);
12453
13111
  }
13112
+ resolveSkillRegistry() {
13113
+ if (this.skills.registry) {
13114
+ if (this.skills.skillDirs.length > 0) {
13115
+ for (const dir of this.skills.skillDirs) {
13116
+ const skills = loadSkillsFromDirectory(dir, { type: "directory", path: dir });
13117
+ this.skills.registry.registerMany(skills);
13118
+ }
13119
+ }
13120
+ return this.skills.registry;
13121
+ }
13122
+ if (this.skills.skillDirs.length > 0) {
13123
+ const { SkillRegistry: SR } = (init_registry2(), __toCommonJS(registry_exports));
13124
+ const reg = new SR();
13125
+ for (const dir of this.skills.skillDirs) {
13126
+ const skills = loadSkillsFromDirectory(dir, { type: "directory", path: dir });
13127
+ reg.registerMany(skills);
13128
+ }
13129
+ return reg;
13130
+ }
13131
+ return void 0;
13132
+ }
13133
+ /**
13134
+ * Resolve pre-activated skill instructions synchronously.
13135
+ * Reads SKILL.md from disk via readFileSync (skills are local files).
13136
+ */
13137
+ resolvePreActivatedInstructions(skillRegistry) {
13138
+ if (this.skills.preActivated.length === 0) return void 0;
13139
+ const fs4 = require("fs");
13140
+ const blocks = [];
13141
+ for (const { name, args } of this.skills.preActivated) {
13142
+ const skill = skillRegistry.get(name);
13143
+ if (!skill) continue;
13144
+ const content = fs4.readFileSync(skill.sourcePath, "utf-8");
13145
+ const { body } = parseFrontmatter(content);
13146
+ const resolved = resolveInstructions(body, {
13147
+ arguments: args,
13148
+ variables: { SKILL_DIR: skill.sourceDir, CLAUDE_SKILL_DIR: skill.sourceDir },
13149
+ cwd: skill.sourceDir,
13150
+ shell: skill.metadata.shell
13151
+ });
13152
+ blocks.push(`## Skill: ${name}
13153
+
13154
+ ${resolved}`);
13155
+ }
13156
+ return blocks.length > 0 ? blocks.join("\n\n---\n\n") : void 0;
13157
+ }
12454
13158
  buildAgentOptions(userPrompt) {
12455
13159
  if (!this.core.client) {
12456
13160
  const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
12457
13161
  this.core.client = new LLMistClass();
12458
13162
  }
12459
13163
  const registry = GadgetRegistry.from(this.gadgets.gadgets);
13164
+ let systemPrompt = this.core.systemPrompt;
13165
+ const skillRegistry = this.resolveSkillRegistry();
13166
+ if (skillRegistry && skillRegistry.size > 0) {
13167
+ if (skillRegistry.getModelInvocable().length > 0) {
13168
+ registry.registerByClass(createUseSkillGadget(skillRegistry));
13169
+ }
13170
+ const preActivatedBlock = this.resolvePreActivatedInstructions(skillRegistry);
13171
+ if (preActivatedBlock) {
13172
+ systemPrompt = systemPrompt ? `${systemPrompt}
13173
+
13174
+ ${preActivatedBlock}` : preActivatedBlock;
13175
+ }
13176
+ }
12460
13177
  return {
12461
13178
  client: this.core.client,
12462
13179
  model: this.core.model ?? "openai:gpt-5-nano",
12463
- systemPrompt: this.core.systemPrompt,
13180
+ systemPrompt,
12464
13181
  userPrompt,
12465
13182
  registry,
12466
13183
  maxIterations: this.core.maxIterations,
@@ -13145,8 +13862,8 @@ var init_error_formatter = __esm({
13145
13862
  const parts = [];
13146
13863
  parts.push(`Error: Invalid parameters for '${gadgetName}':`);
13147
13864
  for (const issue of zodError.issues) {
13148
- const path = issue.path.join(".") || "root";
13149
- parts.push(` - ${path}: ${issue.message}`);
13865
+ const path3 = issue.path.join(".") || "root";
13866
+ parts.push(` - ${path3}: ${issue.message}`);
13150
13867
  }
13151
13868
  parts.push("");
13152
13869
  parts.push("Gadget Usage:");
@@ -13211,7 +13928,7 @@ function stripMarkdownFences(content) {
13211
13928
  return cleaned.trim();
13212
13929
  }
13213
13930
  var globalInvocationCounter, GadgetCallParser;
13214
- var init_parser = __esm({
13931
+ var init_parser2 = __esm({
13215
13932
  "src/gadgets/parser.ts"() {
13216
13933
  "use strict";
13217
13934
  init_constants();
@@ -13247,8 +13964,9 @@ var init_parser = __esm({
13247
13964
  * - `GadgetName` - Auto-generate ID, no dependencies
13248
13965
  * - `GadgetName:my_id` - Explicit ID, no dependencies
13249
13966
  * - `GadgetName:my_id:dep1,dep2` - Explicit ID with dependencies
13967
+ * - `GadgetName:my_id:dep1:dep2:dep3` - Colons treated as dep separators (LLM resilience)
13250
13968
  *
13251
- * Dependencies must be comma-separated invocation IDs.
13969
+ * Dependencies can be comma-separated or colon-separated invocation IDs.
13252
13970
  */
13253
13971
  parseInvocationMetadata(headerLine) {
13254
13972
  const parts = headerLine.split(":");
@@ -13265,7 +13983,12 @@ var init_parser = __esm({
13265
13983
  dependencies: []
13266
13984
  };
13267
13985
  } else {
13268
- const deps = parts[2].split(",").map((d) => d.trim()).filter((d) => d.length > 0);
13986
+ const depsRaw = parts.slice(2).join(",");
13987
+ const deps = [
13988
+ ...new Set(
13989
+ depsRaw.split(",").map((d) => d.trim()).filter((d) => d.length > 0)
13990
+ )
13991
+ ];
13269
13992
  return {
13270
13993
  gadgetName: parts[0],
13271
13994
  invocationId: parts[1].trim(),
@@ -13423,15 +14146,15 @@ function getHostExportsInternal() {
13423
14146
  createGadget,
13424
14147
  ExecutionTree,
13425
14148
  LLMist,
13426
- z: import_zod2.z
14149
+ z: import_zod3.z
13427
14150
  };
13428
14151
  }
13429
- var import_fast_deep_equal, import_zod2, GadgetExecutor;
14152
+ var import_fast_deep_equal, import_zod3, GadgetExecutor;
13430
14153
  var init_executor = __esm({
13431
14154
  "src/gadgets/executor.ts"() {
13432
14155
  "use strict";
13433
14156
  import_fast_deep_equal = __toESM(require("fast-deep-equal"), 1);
13434
- import_zod2 = require("zod");
14157
+ import_zod3 = require("zod");
13435
14158
  init_builder();
13436
14159
  init_hook_utils();
13437
14160
  init_client();
@@ -13443,7 +14166,7 @@ var init_executor = __esm({
13443
14166
  init_create_gadget();
13444
14167
  init_error_formatter();
13445
14168
  init_exceptions();
13446
- init_parser();
14169
+ init_parser2();
13447
14170
  init_typed_gadget();
13448
14171
  GadgetExecutor = class {
13449
14172
  registry;
@@ -13838,6 +14561,8 @@ var init_gadget_concurrency_manager = __esm({
13838
14561
  concurrencyQueue = /* @__PURE__ */ new Map();
13839
14562
  /** All active gadget promises, keyed by invocationId */
13840
14563
  inFlightExecutions = /* @__PURE__ */ new Map();
14564
+ /** Gadget name for each in-flight invocationId (for timeout event emission) */
14565
+ inFlightGadgetNames = /* @__PURE__ */ new Map();
13841
14566
  /** Queue of exclusive gadgets deferred until in-flight gadgets complete */
13842
14567
  exclusiveQueue = [];
13843
14568
  constructor(options) {
@@ -13965,6 +14690,7 @@ var init_gadget_concurrency_manager = __esm({
13965
14690
  const currentCount = this.activeCountByGadget.get(gadgetName) ?? 0;
13966
14691
  this.activeCountByGadget.set(gadgetName, currentCount + 1);
13967
14692
  this.inFlightExecutions.set(invocationId, promise);
14693
+ this.inFlightGadgetNames.set(invocationId, gadgetName);
13968
14694
  }
13969
14695
  /**
13970
14696
  * Called when a gadget execution completes.
@@ -14008,11 +14734,25 @@ var init_gadget_concurrency_manager = __esm({
14008
14734
  this.exclusiveQueue.push(call);
14009
14735
  }
14010
14736
  /**
14011
- * Clear the inFlightExecutions map after all promises have completed.
14012
- * Called after waitForAll resolves.
14737
+ * Clear all in-flight tracking state.
14738
+ * Called after all promises have completed, or on force-timeout.
14739
+ * Also clears active gadget counts to prevent stale state when
14740
+ * hanging promises eventually resolve into the void.
14013
14741
  */
14014
14742
  clearInFlight() {
14015
14743
  this.inFlightExecutions.clear();
14744
+ this.inFlightGadgetNames.clear();
14745
+ this.activeCountByGadget.clear();
14746
+ }
14747
+ /**
14748
+ * Get metadata for all currently in-flight executions.
14749
+ * Used by the dispatcher to emit skip events when the in-flight timeout fires.
14750
+ */
14751
+ getInFlightEntries() {
14752
+ return Array.from(this.inFlightGadgetNames.entries()).map(([invocationId, gadgetName]) => ({
14753
+ invocationId,
14754
+ gadgetName
14755
+ }));
14016
14756
  }
14017
14757
  // ==========================================================================
14018
14758
  // Waiting
@@ -14337,6 +15077,7 @@ var init_gadget_dispatcher = __esm({
14337
15077
  logger;
14338
15078
  pushToQueue;
14339
15079
  drainQueue;
15080
+ inFlightTimeoutMs;
14340
15081
  constructor(options) {
14341
15082
  this.iteration = options.iteration;
14342
15083
  this.hookLifecycle = options.hookLifecycle;
@@ -14351,6 +15092,7 @@ var init_gadget_dispatcher = __esm({
14351
15092
  this.logger = options.logger ?? createLogger({ name: "llmist:gadget-dispatcher" });
14352
15093
  this.pushToQueue = options.pushToQueue;
14353
15094
  this.drainQueue = options.drainQueue;
15095
+ this.inFlightTimeoutMs = options.inFlightTimeoutMs ?? 3e5;
14354
15096
  }
14355
15097
  // ==========================================================================
14356
15098
  // Primary dispatch entry point
@@ -14382,39 +15124,54 @@ var init_gadget_dispatcher = __esm({
14382
15124
  parentId: this.parentNodeId
14383
15125
  });
14384
15126
  }
15127
+ let effectiveCall = call;
14385
15128
  if (call.dependencies.length > 0) {
14386
- if (call.dependencies.includes(call.invocationId)) {
14387
- this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
15129
+ const selfRefs = [];
15130
+ const validDeps = [];
15131
+ for (const dep of call.dependencies) {
15132
+ (dep === call.invocationId ? selfRefs : validDeps).push(dep);
15133
+ }
15134
+ if (selfRefs.length > 0) {
15135
+ this.logger.warn("Filtering self-referential dependencies", {
14388
15136
  gadgetName: call.gadgetName,
14389
- invocationId: call.invocationId
15137
+ invocationId: call.invocationId,
15138
+ removedSelfRefs: selfRefs,
15139
+ remainingDeps: validDeps
14390
15140
  });
14391
- const errorMessage = `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`;
14392
- for await (const evt of this.emitGadgetSkipEvents(call, call.invocationId, errorMessage)) {
14393
- yield evt;
15141
+ effectiveCall = { ...call, dependencies: validDeps };
15142
+ if (validDeps.length === 0) {
15143
+ const errorMessage = `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`;
15144
+ for await (const evt of this.emitGadgetSkipEvents(
15145
+ effectiveCall,
15146
+ call.invocationId,
15147
+ errorMessage
15148
+ )) {
15149
+ yield evt;
15150
+ }
15151
+ return;
14394
15152
  }
14395
- return;
14396
15153
  }
14397
- const failedDep = this.dependencyResolver.getFailedDependency(call);
15154
+ const failedDep = this.dependencyResolver.getFailedDependency(effectiveCall);
14398
15155
  if (failedDep) {
14399
- const skipEvents = await this.handleFailedDependency(call, failedDep);
15156
+ const skipEvents = await this.handleFailedDependency(effectiveCall, failedDep);
14400
15157
  for (const evt of skipEvents) {
14401
15158
  yield evt;
14402
15159
  }
14403
15160
  return;
14404
15161
  }
14405
- if (!this.dependencyResolver.isAllSatisfied(call)) {
14406
- const unsatisfied = call.dependencies.filter(
15162
+ if (!this.dependencyResolver.isAllSatisfied(effectiveCall)) {
15163
+ const unsatisfied = effectiveCall.dependencies.filter(
14407
15164
  (dep) => !this.dependencyResolver.isCompleted(dep)
14408
15165
  );
14409
15166
  this.logger.debug("Queueing gadget for later - waiting on dependencies", {
14410
- gadgetName: call.gadgetName,
14411
- invocationId: call.invocationId,
15167
+ gadgetName: effectiveCall.gadgetName,
15168
+ invocationId: effectiveCall.invocationId,
14412
15169
  waitingOn: unsatisfied
14413
15170
  });
14414
- this.dependencyResolver.addPending(call);
15171
+ this.dependencyResolver.addPending(effectiveCall);
14415
15172
  return;
14416
15173
  }
14417
- for await (const evt of this._checkLimitThenExecute(call)) {
15174
+ for await (const evt of this._checkLimitThenExecute(effectiveCall)) {
14418
15175
  yield evt;
14419
15176
  }
14420
15177
  for await (const evt of this.processPendingGadgets()) {
@@ -14553,7 +15310,31 @@ var init_gadget_dispatcher = __esm({
14553
15310
  queuedCount: this.concurrencyManager.getQueuedGadgetCount()
14554
15311
  });
14555
15312
  const POLL_INTERVAL_MS = 100;
15313
+ const startTime = Date.now();
14556
15314
  while (this.concurrencyManager.inFlightCount > 0 || this.concurrencyManager.hasQueuedGadgets()) {
15315
+ if (Date.now() - startTime >= this.inFlightTimeoutMs) {
15316
+ const timedOutEntries = this.concurrencyManager.getInFlightEntries();
15317
+ this.logger.error("In-flight gadget timeout exceeded, force-clearing remaining gadgets", {
15318
+ timeoutMs: this.inFlightTimeoutMs,
15319
+ remainingInFlight: timedOutEntries.length,
15320
+ remainingQueued: this.concurrencyManager.getQueuedGadgetCount(),
15321
+ timedOutInvocations: timedOutEntries.map((e) => e.invocationId)
15322
+ });
15323
+ const timeoutError = `Gadget execution timed out after ${this.inFlightTimeoutMs}ms`;
15324
+ for (const { invocationId, gadgetName } of timedOutEntries) {
15325
+ this.dependencyResolver.markFailed(invocationId);
15326
+ yield {
15327
+ type: "gadget_skipped",
15328
+ gadgetName,
15329
+ invocationId,
15330
+ parameters: {},
15331
+ failedDependency: invocationId,
15332
+ failedDependencyError: timeoutError
15333
+ };
15334
+ }
15335
+ this.concurrencyManager.clearInFlight();
15336
+ break;
15337
+ }
14557
15338
  const allDone = this.concurrencyManager.getAllDonePromise();
14558
15339
  const result = await Promise.race([
14559
15340
  allDone,
@@ -15044,7 +15825,7 @@ var init_stream_processor = __esm({
15044
15825
  "src/agent/stream-processor.ts"() {
15045
15826
  "use strict";
15046
15827
  init_executor();
15047
- init_parser();
15828
+ init_parser2();
15048
15829
  init_logger();
15049
15830
  init_gadget_concurrency_manager();
15050
15831
  init_gadget_dependency_resolver();
@@ -15139,7 +15920,8 @@ var init_stream_processor = __esm({
15139
15920
  const evts = [...this.completedResultsQueue];
15140
15921
  this.completedResultsQueue = [];
15141
15922
  return evts;
15142
- }
15923
+ },
15924
+ inFlightTimeoutMs: options.inFlightTimeoutMs
15143
15925
  });
15144
15926
  }
15145
15927
  /**
@@ -15363,6 +16145,7 @@ var init_stream_processor_factory = __esm({
15363
16145
  logger;
15364
16146
  requestHumanInput;
15365
16147
  defaultGadgetTimeoutMs;
16148
+ inFlightTimeoutMs;
15366
16149
  gadgetExecutionMode;
15367
16150
  client;
15368
16151
  mediaStore;
@@ -15381,6 +16164,7 @@ var init_stream_processor_factory = __esm({
15381
16164
  this.logger = options.logger;
15382
16165
  this.requestHumanInput = options.requestHumanInput;
15383
16166
  this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
16167
+ this.inFlightTimeoutMs = options.inFlightTimeoutMs;
15384
16168
  this.gadgetExecutionMode = options.gadgetExecutionMode;
15385
16169
  this.client = options.client;
15386
16170
  this.mediaStore = options.mediaStore;
@@ -15412,6 +16196,7 @@ var init_stream_processor_factory = __esm({
15412
16196
  logger: this.logger.getSubLogger({ name: "stream-processor" }),
15413
16197
  requestHumanInput: this.requestHumanInput,
15414
16198
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
16199
+ inFlightTimeoutMs: this.inFlightTimeoutMs,
15415
16200
  gadgetExecutionMode: this.gadgetExecutionMode,
15416
16201
  client: this.client,
15417
16202
  mediaStore: this.mediaStore,
@@ -16206,11 +16991,14 @@ __export(index_exports, {
16206
16991
  OpenRouterProvider: () => OpenRouterProvider,
16207
16992
  RateLimitTracker: () => RateLimitTracker,
16208
16993
  SimpleSessionManager: () => SimpleSessionManager,
16994
+ Skill: () => Skill,
16995
+ SkillRegistry: () => SkillRegistry,
16209
16996
  SlidingWindowStrategy: () => SlidingWindowStrategy,
16210
16997
  StreamProcessor: () => StreamProcessor,
16211
16998
  SummarizationStrategy: () => SummarizationStrategy,
16212
16999
  TaskCompletionSignal: () => TaskCompletionSignal,
16213
17000
  TimeoutException: () => TimeoutException,
17001
+ USE_SKILL_GADGET_NAME: () => USE_SKILL_GADGET_NAME,
16214
17002
  audioFromBase64: () => audioFromBase64,
16215
17003
  audioFromBuffer: () => audioFromBuffer,
16216
17004
  collectEvents: () => collectEvents,
@@ -16228,10 +17016,12 @@ __export(index_exports, {
16228
17016
  createOpenAIProviderFromEnv: () => createOpenAIProviderFromEnv,
16229
17017
  createOpenRouterProviderFromEnv: () => createOpenRouterProviderFromEnv,
16230
17018
  createSubagent: () => createSubagent,
17019
+ createUseSkillGadget: () => createUseSkillGadget,
16231
17020
  defaultLogger: () => defaultLogger,
16232
17021
  detectAudioMimeType: () => detectAudioMimeType,
16233
17022
  detectImageMimeType: () => detectImageMimeType,
16234
17023
  discoverProviderAdapters: () => discoverProviderAdapters,
17024
+ discoverSkills: () => discoverSkills,
16235
17025
  extractMessageText: () => extractMessageText,
16236
17026
  extractRetryAfterMs: () => extractRetryAfterMs,
16237
17027
  filterByDepth: () => filterByDepth,
@@ -16274,15 +17064,21 @@ __export(index_exports, {
16274
17064
  iterationProgressHint: () => iterationProgressHint,
16275
17065
  listPresets: () => listPresets,
16276
17066
  listSubagents: () => listSubagents,
17067
+ loadSkillsFromDirectory: () => loadSkillsFromDirectory,
16277
17068
  normalizeMessageContent: () => normalizeMessageContent,
16278
17069
  parallelGadgetHint: () => parallelGadgetHint,
16279
17070
  parseDataUrl: () => parseDataUrl,
17071
+ parseFrontmatter: () => parseFrontmatter,
16280
17072
  parseManifest: () => parseManifest,
17073
+ parseMetadata: () => parseMetadata,
16281
17074
  parseRetryAfterHeader: () => parseRetryAfterHeader,
17075
+ parseSkillContent: () => parseSkillContent,
17076
+ parseSkillFile: () => parseSkillFile,
16282
17077
  randomDelay: () => randomDelay,
16283
17078
  resetFileLoggingState: () => resetFileLoggingState,
16284
17079
  resolveConfig: () => resolveConfig,
16285
17080
  resolveHintTemplate: () => resolveHintTemplate,
17081
+ resolveInstructions: () => resolveInstructions,
16286
17082
  resolveModel: () => resolveModel,
16287
17083
  resolvePromptTemplate: () => resolvePromptTemplate,
16288
17084
  resolveRateLimitConfig: () => resolveRateLimitConfig,
@@ -16297,9 +17093,12 @@ __export(index_exports, {
16297
17093
  resultWithImages: () => resultWithImages,
16298
17094
  resultWithMedia: () => resultWithMedia,
16299
17095
  runWithHandlers: () => runWithHandlers,
17096
+ scanResources: () => scanResources,
16300
17097
  schemaToJSONSchema: () => schemaToJSONSchema,
16301
17098
  stream: () => stream,
16302
17099
  stripProviderPrefix: () => stripProviderPrefix,
17100
+ substituteArguments: () => substituteArguments,
17101
+ substituteVariables: () => substituteVariables,
16303
17102
  text: () => text,
16304
17103
  timing: () => timing,
16305
17104
  toBase64: () => toBase64,
@@ -16307,13 +17106,14 @@ __export(index_exports, {
16307
17106
  validateAndApplyDefaults: () => validateAndApplyDefaults,
16308
17107
  validateGadgetParams: () => validateGadgetParams,
16309
17108
  validateGadgetSchema: () => validateGadgetSchema,
17109
+ validateMetadata: () => validateMetadata,
16310
17110
  withErrorHandling: () => withErrorHandling,
16311
17111
  withRetry: () => withRetry,
16312
17112
  withTimeout: () => withTimeout,
16313
- z: () => import_zod3.z
17113
+ z: () => import_zod4.z
16314
17114
  });
16315
17115
  module.exports = __toCommonJS(index_exports);
16316
- var import_zod3 = require("zod");
17116
+ var import_zod4 = require("zod");
16317
17117
  init_agent();
16318
17118
  init_builder();
16319
17119
  init_event_handlers();
@@ -16627,7 +17427,7 @@ function resultWithFile(result, fileData, mimeType, options) {
16627
17427
 
16628
17428
  // src/index.ts
16629
17429
  init_output_viewer();
16630
- init_parser();
17430
+ init_parser2();
16631
17431
  init_registry();
16632
17432
  init_typed_gadget();
16633
17433
  init_constants2();
@@ -16894,6 +17694,14 @@ var SimpleSessionManager = class extends BaseSessionManager {
16894
17694
  }
16895
17695
  };
16896
17696
 
17697
+ // src/skills/index.ts
17698
+ init_activation();
17699
+ init_loader();
17700
+ init_parser();
17701
+ init_registry2();
17702
+ init_skill();
17703
+ init_use_skill_gadget();
17704
+
16897
17705
  // src/utils/format.ts
16898
17706
  function truncate(text3, maxLength, suffix = "...") {
16899
17707
  if (text3.length <= maxLength) return text3;
@@ -17090,11 +17898,14 @@ function getHostExports2(ctx) {
17090
17898
  OpenRouterProvider,
17091
17899
  RateLimitTracker,
17092
17900
  SimpleSessionManager,
17901
+ Skill,
17902
+ SkillRegistry,
17093
17903
  SlidingWindowStrategy,
17094
17904
  StreamProcessor,
17095
17905
  SummarizationStrategy,
17096
17906
  TaskCompletionSignal,
17097
17907
  TimeoutException,
17908
+ USE_SKILL_GADGET_NAME,
17098
17909
  audioFromBase64,
17099
17910
  audioFromBuffer,
17100
17911
  collectEvents,
@@ -17112,10 +17923,12 @@ function getHostExports2(ctx) {
17112
17923
  createOpenAIProviderFromEnv,
17113
17924
  createOpenRouterProviderFromEnv,
17114
17925
  createSubagent,
17926
+ createUseSkillGadget,
17115
17927
  defaultLogger,
17116
17928
  detectAudioMimeType,
17117
17929
  detectImageMimeType,
17118
17930
  discoverProviderAdapters,
17931
+ discoverSkills,
17119
17932
  extractMessageText,
17120
17933
  extractRetryAfterMs,
17121
17934
  filterByDepth,
@@ -17158,15 +17971,21 @@ function getHostExports2(ctx) {
17158
17971
  iterationProgressHint,
17159
17972
  listPresets,
17160
17973
  listSubagents,
17974
+ loadSkillsFromDirectory,
17161
17975
  normalizeMessageContent,
17162
17976
  parallelGadgetHint,
17163
17977
  parseDataUrl,
17978
+ parseFrontmatter,
17164
17979
  parseManifest,
17980
+ parseMetadata,
17165
17981
  parseRetryAfterHeader,
17982
+ parseSkillContent,
17983
+ parseSkillFile,
17166
17984
  randomDelay,
17167
17985
  resetFileLoggingState,
17168
17986
  resolveConfig,
17169
17987
  resolveHintTemplate,
17988
+ resolveInstructions,
17170
17989
  resolveModel,
17171
17990
  resolvePromptTemplate,
17172
17991
  resolveRateLimitConfig,
@@ -17181,9 +18000,12 @@ function getHostExports2(ctx) {
17181
18000
  resultWithImages,
17182
18001
  resultWithMedia,
17183
18002
  runWithHandlers,
18003
+ scanResources,
17184
18004
  schemaToJSONSchema,
17185
18005
  stream,
17186
18006
  stripProviderPrefix,
18007
+ substituteArguments,
18008
+ substituteVariables,
17187
18009
  text,
17188
18010
  timing,
17189
18011
  toBase64,
@@ -17191,6 +18013,7 @@ function getHostExports2(ctx) {
17191
18013
  validateAndApplyDefaults,
17192
18014
  validateGadgetParams,
17193
18015
  validateGadgetSchema,
18016
+ validateMetadata,
17194
18017
  withErrorHandling,
17195
18018
  withRetry,
17196
18019
  withTimeout,