zidane 4.0.2 → 4.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +196 -614
  2. package/dist/agent-BoV5Twdl.d.ts +2347 -0
  3. package/dist/agent-BoV5Twdl.d.ts.map +1 -0
  4. package/dist/contexts-3Arvn7yR.js +321 -0
  5. package/dist/contexts-3Arvn7yR.js.map +1 -0
  6. package/dist/contexts.d.ts +2 -25
  7. package/dist/contexts.js +2 -10
  8. package/dist/errors-D1lhd6mX.js +118 -0
  9. package/dist/errors-D1lhd6mX.js.map +1 -0
  10. package/dist/index-28otmfLX.d.ts +400 -0
  11. package/dist/index-28otmfLX.d.ts.map +1 -0
  12. package/dist/index-BfSdALzk.d.ts +113 -0
  13. package/dist/index-BfSdALzk.d.ts.map +1 -0
  14. package/dist/index-DPsd0qwm.d.ts +254 -0
  15. package/dist/index-DPsd0qwm.d.ts.map +1 -0
  16. package/dist/index.d.ts +5 -95
  17. package/dist/index.js +141 -271
  18. package/dist/index.js.map +1 -0
  19. package/dist/interpolate-CukJwP2G.js +887 -0
  20. package/dist/interpolate-CukJwP2G.js.map +1 -0
  21. package/dist/mcp-8wClKY-3.js +771 -0
  22. package/dist/mcp-8wClKY-3.js.map +1 -0
  23. package/dist/mcp.d.ts +2 -4
  24. package/dist/mcp.js +2 -13
  25. package/dist/messages-z5Pq20p7.js +1020 -0
  26. package/dist/messages-z5Pq20p7.js.map +1 -0
  27. package/dist/presets-Cs7_CsMk.js +39 -0
  28. package/dist/presets-Cs7_CsMk.js.map +1 -0
  29. package/dist/presets.d.ts +2 -43
  30. package/dist/presets.js +2 -17
  31. package/dist/providers-CX-R-Oy-.js +969 -0
  32. package/dist/providers-CX-R-Oy-.js.map +1 -0
  33. package/dist/providers.d.ts +2 -4
  34. package/dist/providers.js +3 -23
  35. package/dist/session/sqlite.d.ts +7 -12
  36. package/dist/session/sqlite.d.ts.map +1 -0
  37. package/dist/session/sqlite.js +67 -79
  38. package/dist/session/sqlite.js.map +1 -0
  39. package/dist/session-Cn68UASv.js +440 -0
  40. package/dist/session-Cn68UASv.js.map +1 -0
  41. package/dist/session.d.ts +2 -4
  42. package/dist/session.js +3 -27
  43. package/dist/skills.d.ts +3 -322
  44. package/dist/skills.js +24 -47
  45. package/dist/skills.js.map +1 -0
  46. package/dist/stats-DoKUtF5T.js +58 -0
  47. package/dist/stats-DoKUtF5T.js.map +1 -0
  48. package/dist/tools-DpeWKzP1.js +3941 -0
  49. package/dist/tools-DpeWKzP1.js.map +1 -0
  50. package/dist/tools.d.ts +3 -95
  51. package/dist/tools.js +2 -40
  52. package/dist/tui.d.ts +533 -0
  53. package/dist/tui.d.ts.map +1 -0
  54. package/dist/tui.js +2004 -0
  55. package/dist/tui.js.map +1 -0
  56. package/dist/types-Bx_F8jet.js +39 -0
  57. package/dist/types-Bx_F8jet.js.map +1 -0
  58. package/dist/types.d.ts +4 -55
  59. package/dist/types.js +4 -28
  60. package/package.json +38 -4
  61. package/dist/agent-BAHrGtqu.d.ts +0 -2425
  62. package/dist/chunk-4ILGBQ23.js +0 -803
  63. package/dist/chunk-4LPBN547.js +0 -3540
  64. package/dist/chunk-64LLNY7F.js +0 -28
  65. package/dist/chunk-6STZTA4N.js +0 -830
  66. package/dist/chunk-7GQ7P6DM.js +0 -566
  67. package/dist/chunk-IC7FT4OD.js +0 -37
  68. package/dist/chunk-JCOB6IYO.js +0 -22
  69. package/dist/chunk-JH6IAAFA.js +0 -28
  70. package/dist/chunk-LNN5UTS2.js +0 -97
  71. package/dist/chunk-PMCQOMV4.js +0 -490
  72. package/dist/chunk-UD25QF3H.js +0 -304
  73. package/dist/chunk-W57VY6DJ.js +0 -834
  74. package/dist/sandbox-D7v6Wy62.d.ts +0 -28
  75. package/dist/skills-use-DwZrNmcw.d.ts +0 -80
  76. package/dist/types-Bai5rKpa.d.ts +0 -89
  77. package/dist/validation-Pm--dQEU.d.ts +0 -185
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interpolate-CukJwP2G.js","names":[],"sources":["../src/skills/activation.ts","../src/skills/validate.ts","../src/skills/allowed-tools.ts","../src/xml.ts","../src/skills/catalog.ts","../src/skills/discovery.ts","../src/skills/writer.ts","../src/skills/resolve.ts","../src/skills/interpolate.ts"],"sourcesContent":["/**\n * Per-agent skill activation state machine.\n *\n * Tracks which skills are active across a run. The three skills tools\n * (`skills_use` / `skills_read` / `skills_run_script`) read from this state\n * for gating + listing. Allowed-tools enforcement reads from it too.\n *\n * Lifecycle:\n * - Storage lives on the agent instance (created once in `createAgent`), but\n * every `run()` ends with an implicit deactivate-all pass (reason `'run-end'`)\n * so activation does **not** persist across run boundaries. To keep a skill\n * active across successive runs, call `agent.activateSkill(name)` before each\n * run — the explicit-activation path is idempotent.\n * - `agent.reset()` clears the state with reason `'reset'`.\n * - On session-resume, carried-forward `skills_use` tool_call blocks (in prior\n * assistant turns) are replayed at run-start to rehydrate state with\n * `via: 'resume'`.\n *\n * The caps (`maxActive` from SkillsConfig) is enforced here — returning `false`\n * from `activate()` when the cap is hit lets the caller surface an actionable\n * \"max skills active\" error to the model.\n */\n\nimport type { SkillConfig } from './types'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** How a skill was activated. Surfaced in `skills:activate` hook ctx. */\nexport type ActivationVia = 'model' | 'explicit' | 'resume'\n\n/** Reason a skill was deactivated. Surfaced in `skills:deactivate` hook ctx. */\nexport type DeactivationReason = 'run-end' | 'explicit' | 'reset'\n\n/** A skill currently active in the state machine. */\nexport interface ActiveSkill {\n skill: SkillConfig\n activatedAt: number\n activatedVia: ActivationVia\n}\n\n/**\n * Per-agent skill activation state. Public read-surface is the `active()` list\n * and `isActive(name)` predicate; writes go through `activate()` / `deactivate()`.\n */\nexport interface SkillActivationState {\n /** List of currently active skills in activation order. Returns a snapshot. */\n active: () => readonly ActiveSkill[]\n /** Is the skill with this canonical name currently active? */\n isActive: (name: string) => boolean\n /** Retrieve the `ActiveSkill` record by name, or `undefined`. */\n get: (name: string) => ActiveSkill | undefined\n /**\n * Mark a skill as active.\n * - Returns `'ok'` on a fresh activation (caller should fire `skills:activate`).\n * - Returns `'already-active'` if the skill was already in the set (idempotent).\n * - Returns `'cap-reached'` if the `maxActive` cap would be exceeded. State is unchanged.\n */\n activate: (skill: SkillConfig, via: ActivationVia) => 'ok' | 'already-active' | 'cap-reached'\n /**\n * Mark a skill as inactive. Returns the removed `ActiveSkill` record or `undefined`\n * if it wasn't active. Callers fire `skills:deactivate` on removal.\n */\n deactivate: (name: string) => ActiveSkill | undefined\n /** Remove every active skill. Returns the list of removed records. */\n clear: () => readonly ActiveSkill[]\n}\n\nexport interface SkillActivationStateOptions {\n /**\n * Cap on concurrent activations. `undefined` (the default) disables the cap.\n * When set, `activate()` returns `'cap-reached'` once the set is at size `maxActive`.\n */\n maxActive?: number\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createSkillActivationState(\n options: SkillActivationStateOptions = {},\n): SkillActivationState {\n const byName = new Map<string, ActiveSkill>()\n const maxActive = typeof options.maxActive === 'number' && options.maxActive > 0\n ? options.maxActive\n : undefined\n\n return {\n active() {\n return [...byName.values()]\n },\n\n isActive(name) {\n return byName.has(name)\n },\n\n get(name) {\n return byName.get(name)\n },\n\n activate(skill, via) {\n if (byName.has(skill.name))\n return 'already-active'\n if (maxActive !== undefined && byName.size >= maxActive)\n return 'cap-reached'\n byName.set(skill.name, {\n skill,\n activatedAt: Date.now(),\n activatedVia: via,\n })\n return 'ok'\n },\n\n deactivate(name) {\n const existing = byName.get(name)\n if (!existing)\n return undefined\n byName.delete(name)\n return existing\n },\n\n clear() {\n const snapshot = [...byName.values()]\n byName.clear()\n return snapshot\n },\n }\n}\n","/**\n * Strict validators for Agent Skills.\n *\n * Applied on the authoring path (`defineSkill`, `writeSkillToDisk`). Parser-time\n * validation is intentionally lenient — see `parseSkillFile` for diagnostic collection.\n */\n\nimport type { SkillConfig } from './types'\nimport { basename } from 'node:path'\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst NAME_MAX = 64\nconst DESCRIPTION_MAX = 1024\nconst COMPATIBILITY_MAX = 500\n\nconst SKILL_NAME_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/\nconst CONSECUTIVE_HYPHENS_RE = /--/\n\n// Accepts both Claude-Code-style PascalCase (`Bash`, `Read`) AND snake_case\n// (`skills_use`, `read_file`, `mcp_fs_read`) tool names. Spec leaves the\n// naming convention to the client.\nconst ALLOWED_TOOL_PATTERN_RE = /^([\\w-]+)(?:\\(([^)]*)\\))?$/\n\n// Path-sandbox regexes (hoisted to avoid per-call recompilation).\nconst ABS_WINDOWS_PATH_RE = /^[a-z]:[\\\\/]/i\nconst PATH_SEPARATOR_RE = /[\\\\/]/\nconst TRAILING_SLASHES_RE = /\\/+$/\n\n// ---------------------------------------------------------------------------\n// Validation result\n// ---------------------------------------------------------------------------\n\n/**\n * A single issue surfaced by skill-authoring validation.\n * Distinct from `ValidationResult` in `tools/validation.ts` (tool-input JSON\n * schema validation) — different domain, different module.\n */\nexport interface SkillValidationIssue {\n /** Stable machine-readable code for consumer matching. */\n code: string\n /** Human-readable description. */\n message: string\n /** Frontmatter field name the issue relates to, when applicable. */\n field?: string\n}\n\nexport interface SkillValidationResult {\n valid: boolean\n errors: SkillValidationIssue[]\n}\n\n// ---------------------------------------------------------------------------\n// Name validation (strict — spec rules)\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a skill name per the spec:\n * - 1–64 characters\n * - Lowercase alphanumeric + hyphens only\n * - Must not start or end with a hyphen\n * - Must not contain consecutive hyphens\n *\n * The parent-directory match is validated separately (it requires knowing the\n * skill's `location`, which this function does not).\n */\nexport function validateSkillName(name: string): boolean {\n if (typeof name !== 'string')\n return false\n if (name.length < 1 || name.length > NAME_MAX)\n return false\n if (CONSECUTIVE_HYPHENS_RE.test(name))\n return false\n return SKILL_NAME_RE.test(name)\n}\n\n// ---------------------------------------------------------------------------\n// Authoring-time validation (`defineSkill`, `writeSkillToDisk`)\n// ---------------------------------------------------------------------------\n\n/**\n * Strict validation for a skill that is about to be authored / written to disk.\n * Rejects anything that would violate the spec. Use the lenient parser path\n * (`parseSkillFile`) for loading third-party skills.\n */\nexport function validateSkillForWrite(skill: SkillConfig): SkillValidationResult {\n const errors: SkillValidationIssue[] = []\n\n // Name\n if (!validateSkillName(skill.name)) {\n errors.push({\n code: 'invalid-name',\n message: `Skill name \"${skill.name}\" must be 1-64 chars, lowercase alphanumeric + hyphens, no leading/trailing/consecutive hyphens.`,\n field: 'name',\n })\n }\n\n // Name must match parent directory when location is present\n if (skill.location) {\n const dirName = basename(skill.baseDir ?? '')\n if (dirName && dirName !== skill.name) {\n errors.push({\n code: 'name-mismatch-directory',\n message: `Skill name \"${skill.name}\" must match parent directory name \"${dirName}\".`,\n field: 'name',\n })\n }\n }\n\n // Description\n if (typeof skill.description !== 'string' || skill.description.length < 1) {\n errors.push({\n code: 'missing-description',\n message: 'Skill description is required (non-empty).',\n field: 'description',\n })\n }\n else if (skill.description.length > DESCRIPTION_MAX) {\n errors.push({\n code: 'description-too-long',\n message: `Skill description must be at most ${DESCRIPTION_MAX} characters (got ${skill.description.length}).`,\n field: 'description',\n })\n }\n\n // Compatibility (optional)\n if (skill.compatibility !== undefined) {\n if (typeof skill.compatibility !== 'string' || skill.compatibility.length === 0) {\n errors.push({\n code: 'invalid-compatibility',\n message: 'Compatibility must be a non-empty string when provided.',\n field: 'compatibility',\n })\n }\n else if (skill.compatibility.length > COMPATIBILITY_MAX) {\n errors.push({\n code: 'compatibility-too-long',\n message: `Compatibility must be at most ${COMPATIBILITY_MAX} characters (got ${skill.compatibility.length}).`,\n field: 'compatibility',\n })\n }\n }\n\n // Metadata values must be strings per spec\n if (skill.metadata) {\n for (const [key, value] of Object.entries(skill.metadata)) {\n if (typeof value !== 'string') {\n errors.push({\n code: 'invalid-metadata-value',\n message: `Metadata value for \"${key}\" must be a string (spec: \"A map from string keys to string values\").`,\n field: 'metadata',\n })\n }\n }\n }\n\n // Allowed-tools entries must be parseable patterns\n if (skill.allowedTools) {\n for (const pattern of skill.allowedTools) {\n if (!ALLOWED_TOOL_PATTERN_RE.test(pattern)) {\n errors.push({\n code: 'invalid-allowed-tool-pattern',\n message: `Allowed-tools entry \"${pattern}\" is not a recognized pattern (expected \"ToolName\" or \"ToolName(arg:*)\").`,\n field: 'allowed-tools',\n })\n }\n }\n }\n\n return { valid: errors.length === 0, errors }\n}\n\n// ---------------------------------------------------------------------------\n// Resource path sandbox\n// ---------------------------------------------------------------------------\n\n/**\n * Validate that `relPath` stays inside `baseDir` when resolved.\n *\n * Rejects:\n * - Absolute paths (`/etc/passwd`, `C:\\…`)\n * - Parent traversal (`..` segments that escape baseDir)\n * - Null-byte tricks\n *\n * Returns `{ valid: true, absolutePath }` on success or `{ valid: false, error }`\n * with a human-readable reason.\n */\nexport function validateResourcePath(\n relPath: string,\n baseDir: string,\n): { valid: true, absolutePath: string } | { valid: false, error: string } {\n if (typeof relPath !== 'string' || relPath.length === 0)\n return { valid: false, error: 'Resource path must be a non-empty string.' }\n\n if (relPath.includes('\\0'))\n return { valid: false, error: 'Resource path contains a null byte.' }\n\n // Reject absolute paths upfront — both POSIX and Windows forms.\n if (relPath.startsWith('/') || ABS_WINDOWS_PATH_RE.test(relPath))\n return { valid: false, error: `Absolute paths are not allowed (\"${relPath}\").` }\n\n // Normalize and ensure we stay under baseDir.\n const segments: string[] = []\n for (const segment of relPath.split(PATH_SEPARATOR_RE)) {\n if (segment === '' || segment === '.')\n continue\n if (segment === '..') {\n if (segments.length === 0) {\n return {\n valid: false,\n error: `Resource path \"${relPath}\" escapes the skill directory.`,\n }\n }\n segments.pop()\n continue\n }\n segments.push(segment)\n }\n\n if (segments.length === 0)\n return { valid: false, error: 'Resource path resolves to the skill root itself.' }\n\n const absolutePath = `${baseDir.replace(TRAILING_SLASHES_RE, '')}/${segments.join('/')}`\n return { valid: true, absolutePath }\n}\n\n// ---------------------------------------------------------------------------\n// Allowed-tools matching\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a single `allowed-tools` entry into its tool name + optional argument pattern.\n *\n * Examples:\n * - `Read` → `{ tool: 'Read' }`\n * - `Bash(git:*)` → `{ tool: 'Bash', argPrefix: 'git' }`\n */\nexport function parseAllowedToolPattern(entry: string): { tool: string, argPrefix?: string } | null {\n const m = entry.trim().match(ALLOWED_TOOL_PATTERN_RE)\n if (!m)\n return null\n const tool = m[1]\n const arg = m[2]\n if (!arg)\n return { tool }\n\n // Accept `git:*` (prefix wildcard) and `git` (literal). Future: full glob.\n if (arg.endsWith(':*'))\n return { tool, argPrefix: arg.slice(0, -2) }\n return { tool, argPrefix: arg }\n}\n\n/**\n * Check whether a tool call (identified by its wire/displayName and argument input)\n * matches a skill's allow-list entry.\n *\n * Matching rules (Zidane's interpretation of the spec's unspecified syntax):\n * - Exact match: displayName === pattern (no parens).\n * - Prefix match: for `Tool(arg:*)`, displayName === 'Tool' AND **any** string\n * value in the input object starts with `arg`. This is intentionally\n * permissive: it doesn't depend on a convention about which property carries\n * the \"command\" (schemas vary across tools), and for the common\n * `shell({ command: 'git …' })` shape it behaves identically to a \"primary\n * string\" rule.\n * - For tools whose inputs carry no string values, the arg-match returns false.\n */\nexport function matchesAllowedTool(\n displayName: string,\n input: Record<string, unknown>,\n pattern: string,\n): boolean {\n const parsed = parseAllowedToolPattern(pattern)\n if (!parsed)\n return false\n if (parsed.tool !== displayName)\n return false\n if (parsed.argPrefix === undefined)\n return true\n\n // Match if any string input value starts with the arg prefix. More robust\n // than picking a single \"first\" value (object property order is a subtle\n // brittleness surface we avoid).\n for (const value of Object.values(input)) {\n if (typeof value === 'string' && value.startsWith(parsed.argPrefix))\n return true\n }\n return false\n}\n\n/**\n * Test whether a tool call is allowed by the union of `allowedTools` across a set\n * of active skills. Returns `true` when the union is empty (permissive default)\n * OR when any entry matches.\n */\nexport function isToolAllowedByUnion(\n displayName: string,\n input: Record<string, unknown>,\n union: readonly string[],\n): boolean {\n if (union.length === 0)\n return true\n return union.some(pattern => matchesAllowedTool(displayName, input, pattern))\n}\n","/**\n * `allowed-tools` enforcement middleware.\n *\n * Installed by the agent on `tool:gate` and `mcp:tool:gate`. Computes the\n * union of `allowedTools` patterns across currently-active skills; blocks any\n * tool call whose `displayName` is not in that union. The three injected\n * skills tools (`skills_use` / `skills_read` / `skills_run_script`) are\n * implicitly allowed so a skill declaring an allow-list cannot lock out the\n * scaffolding that lets it run at all.\n *\n * The match target is `displayName` (wire / LLM-facing name), not the\n * canonical name, because skill authors write allow-list entries to match\n * what the model sees.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { McpToolHookContext, ToolHookContext } from '../types'\nimport type { SkillActivationState } from './activation'\nimport { AgentToolNotAllowedError } from '../errors'\nimport { isToolAllowedByUnion } from './validate'\n\n/** Tools that are always allowed regardless of the active skills' allow-list. */\nexport const IMPLICITLY_ALLOWED_SKILL_TOOLS: readonly string[] = [\n 'skills_use',\n 'skills_read',\n 'skills_run_script',\n]\n\n/**\n * Register `tool:gate` / `mcp:tool:gate` handlers that enforce the union of\n * `allowedTools` across active skills.\n *\n * No-op when no active skill declares an allow-list (permissive default —\n * matches the spec's \"experimental\" note for `allowed-tools`).\n *\n * Returns an `uninstall` fn. The agent calls this at run end to detach the\n * handlers and prevent cross-run hook leaks.\n */\nexport function installAllowedToolsGate(\n hooks: Hookable<AgentHooks>,\n state: SkillActivationState,\n): () => void {\n function effectiveUnion(): { union: string[], active: string[] } {\n const active = state.active()\n const declared: string[] = []\n for (const record of active) {\n if (record.skill.allowedTools?.length)\n declared.push(...record.skill.allowedTools)\n }\n return {\n union: declared.length > 0 ? [...declared, ...IMPLICITLY_ALLOWED_SKILL_TOOLS] : [],\n active: active.map(a => a.skill.name),\n }\n }\n\n function gateHandler(ctx: ToolHookContext & { block: boolean, reason: string }) {\n const { union, active } = effectiveUnion()\n if (union.length === 0)\n return\n if (isToolAllowedByUnion(ctx.displayName, ctx.input, union))\n return\n const err = new AgentToolNotAllowedError({\n toolName: ctx.name,\n displayName: ctx.displayName,\n allowedUnion: union,\n activeSkills: active,\n })\n ctx.block = true\n ctx.reason = err.message\n }\n\n function mcpGateHandler(ctx: McpToolHookContext & { block: boolean, reason: string }) {\n const { union, active } = effectiveUnion()\n if (union.length === 0)\n return\n if (isToolAllowedByUnion(ctx.displayName, ctx.input, union))\n return\n const err = new AgentToolNotAllowedError({\n toolName: `mcp_${ctx.server}_${ctx.tool}`,\n displayName: ctx.displayName,\n allowedUnion: union,\n activeSkills: active,\n })\n ctx.block = true\n ctx.reason = err.message\n }\n\n const unregisterTool = hooks.hook('tool:gate', gateHandler)\n const unregisterMcp = hooks.hook('mcp:tool:gate', mcpGateHandler)\n\n return function uninstall() {\n unregisterTool()\n unregisterMcp()\n }\n}\n","/**\n * Minimal XML helpers used by catalog/result builders that emit\n * model-facing pseudo-XML (skills catalog, searchable_tools, tool_search\n * results, skill_content wrappers).\n *\n * Scope: attribute / text-node escaping only — we do NOT emit a full XML\n * document, the model sees these strings as free-form text. Centralised\n * here so the four near-identical copies in `agent.ts`, `skills/catalog.ts`,\n * `tools/skills-use.ts`, and `tools/tool-search.ts` don't drift.\n */\n\nconst RE_AMP = /&/g\nconst RE_LT = /</g\nconst RE_GT = />/g\nconst RE_QUOT = /\"/g\n\nexport function escapeXml(str: string): string {\n return str\n .replace(RE_AMP, '&amp;')\n .replace(RE_LT, '&lt;')\n .replace(RE_GT, '&gt;')\n .replace(RE_QUOT, '&quot;')\n}\n","/**\n * Skill catalog generation.\n *\n * Builds the system prompt section that discloses available skills to the\n * agent — tier 1 of progressive disclosure per the Agent Skills spec.\n *\n * The catalog's behavioral prose branches on which activation mechanism is\n * active for the run:\n *\n * - When the `skills_use` tool is auto-injected (the default for a non-empty\n * catalog), the prose tells the model to call `skills_use` with the skill's\n * name. The agent returns a structured `<skill_content>` block.\n * - When the skills tool is opted out (`SkillsConfig.tool: false`), the prose\n * falls back to the file-read pattern — the model reads `SKILL.md` itself\n * via its file-read tool.\n */\n\nimport type { SkillConfig } from './types'\nimport { escapeXml } from '../xml'\n\nexport interface BuildCatalogOptions {\n /**\n * When true (the default), the prose instructs the model to call\n * `skills_use`. Set to false when `SkillsConfig.tool === false` so the\n * system prompt matches the active activation mechanism.\n */\n skillsToolRegistered?: boolean\n /**\n * Name of the tool the model should use to read `SKILL.md` files when\n * `skillsToolRegistered` is false. Defaults to `'read_file'`.\n */\n readToolName?: string\n}\n\n/**\n * Build the skill catalog XML and behavioral instructions for the system prompt.\n */\nexport function buildCatalog(\n skills: SkillConfig[],\n options: BuildCatalogOptions = {},\n): string {\n if (skills.length === 0)\n return ''\n\n const skillsToolRegistered = options.skillsToolRegistered ?? true\n const readToolName = options.readToolName ?? 'read_file'\n\n const entries = skills.map((skill) => {\n const locationLine = skill.location\n ? `\\n <location>${escapeXml(skill.location)}</location>`\n : ''\n return ` <skill>\n <name>${escapeXml(skill.name)}</name>\n <description>${escapeXml(skill.description)}</description>${locationLine}\n </skill>`\n }).join('\\n')\n\n // Separate instructions for fs-based vs inline skills\n const hasFsSkills = skills.some(s => s.location)\n const hasInlineSkills = skills.some(s => !s.location)\n\n const behavioralParts: string[] = []\n\n behavioralParts.push(\n 'The following skills provide specialized instructions for specific tasks.',\n 'When a task matches a skill\\'s description, activate the skill to load its full instructions before proceeding.',\n )\n\n if (skillsToolRegistered) {\n behavioralParts.push(\n 'To activate a skill, call the `skills_use` tool with the skill\\'s name. '\n + 'The response contains the full instructions and any bundled resources you can then load '\n + 'via `skills_read` (reference files) or execute via `skills_run_script` (scripts/ directory).',\n 'Relative paths referenced in a skill\\'s instructions resolve against the skill directory noted in the `skills_use` response.',\n )\n }\n else if (hasFsSkills) {\n behavioralParts.push(\n `For skills with a <location>, use the ${readToolName} tool to read the SKILL.md file at that path.`,\n 'When a skill references relative paths, resolve them against the skill\\'s directory (the parent of SKILL.md) and use absolute paths in tool calls.',\n )\n }\n\n if (hasInlineSkills && !skillsToolRegistered) {\n behavioralParts.push(\n 'Skills without a <location> have their instructions included directly in <instructions> tags below.',\n )\n }\n\n // Build the full catalog block\n const parts: string[] = [\n behavioralParts.join('\\n'),\n '',\n '<available_skills>',\n entries,\n '</available_skills>',\n ]\n\n // Append inline skill instructions directly — but only when the model has no\n // other way to reach them (`skills_use` also handles inline skills when\n // registered, so we skip the duplicate copy in that case).\n if (hasInlineSkills && !skillsToolRegistered) {\n parts.push('')\n for (const skill of skills) {\n if (!skill.location && skill.instructions) {\n parts.push(`<skill_instructions name=\"${escapeXml(skill.name)}\">`)\n parts.push(skill.instructions)\n if (skill.resources && skill.resources.length > 0) {\n parts.push('')\n parts.push('<skill_resources>')\n for (const res of skill.resources) {\n parts.push(` <file type=\"${res.type}\">${escapeXml(res.path)}</file>`)\n }\n parts.push('</skill_resources>')\n }\n parts.push('</skill_instructions>')\n }\n }\n }\n\n return parts.join('\\n')\n}\n","/**\n * Skill discovery and parsing.\n *\n * Scans filesystem directories for SKILL.md files following the\n * Agent Skills specification (agentskills.io/specification).\n *\n * Parsing is intentionally **lenient** — per the spec's client-implementation\n * guide: warn, don't block. Diagnostics are attached to the returned\n * SkillConfig for host UIs to surface; only unrecoverable errors (missing\n * description, unparseable YAML) skip the skill entirely.\n */\n\nimport type { SkillConfig, SkillDiagnostic, SkillResource, SkillSource } from './types'\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { basename, dirname, join, resolve } from 'node:path'\nimport { validateSkillName } from './validate'\n\n// ---------------------------------------------------------------------------\n// Regexes\n// ---------------------------------------------------------------------------\n\n// Frontmatter regex — opening ---, content, closing ---, optional body.\n// Tolerates CRLF line endings on the opening and closing fences (authoring\n// tools on Windows sometimes persist them mid-file).\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/\n\nconst INDENT_RE = /^[ \\t]{2,}/\nconst KV_RE = /^([^:]+):(.*)$/\nconst DOUBLE_QUOTED_RE = /^\"((?:\\\\.|[^\"\\\\])*)\"$/\nconst SINGLE_QUOTED_RE = /^'((?:''|[^'])*)'$/\nconst DQ_ESCAPE_RE = /\\\\([\"\\\\/bfnrt])/g\nconst WHITESPACE_SPLIT_RE = /\\s+/\nconst PARAGRAPH_SPLIT_RE = /\\n\\n/\nconst COMMA_OR_SPACE_RE = /[,\\s]+/\n\n// ---------------------------------------------------------------------------\n// Frontmatter parsing\n// ---------------------------------------------------------------------------\n\ninterface ParsedSkillFile {\n frontmatter: Record<string, unknown>\n body: string\n diagnostics: SkillDiagnostic[]\n}\n\n/**\n * Parse a SKILL.md file into frontmatter + body.\n *\n * Uses a simple regex-based YAML extractor that handles:\n * - Flat key: value pairs\n * - Quoted values\n * - One-level nested maps (for `metadata:`)\n * - Lenient recovery from unquoted-colon values (e.g.\n * `description: Use when: the user asks …`) via a quote-wrap retry.\n */\nexport function parseFrontmatter(content: string): ParsedSkillFile {\n const diagnostics: SkillDiagnostic[] = []\n const match = content.match(FRONTMATTER_RE)\n if (!match) {\n return { frontmatter: {}, body: content.trim(), diagnostics }\n }\n\n const yamlBlock = match[1]\n const body = match[2].trim()\n\n // Simple YAML parser for flat and one-level nested key-value pairs\n const frontmatter: Record<string, unknown> = {}\n let currentKey: string | null = null\n let currentMap: Record<string, string> | null = null\n\n for (const line of yamlBlock.split('\\n')) {\n // Skip empty lines and comments\n if (!line.trim() || line.trim().startsWith('#'))\n continue\n\n // Nested key (indented under a map key)\n if (currentKey && currentMap && INDENT_RE.test(line)) {\n const nestedMatch = line.trim().match(KV_RE)\n if (nestedMatch) {\n const val = nestedMatch[2].trim()\n currentMap[nestedMatch[1].trim()] = unquoteYaml(val)\n }\n continue\n }\n\n // Top-level key\n if (currentKey && currentMap) {\n frontmatter[currentKey] = currentMap\n currentKey = null\n currentMap = null\n }\n\n // Split on first colon only — lenient for values containing colons.\n const kvMatch = matchFirstColon(line)\n if (!kvMatch)\n continue\n\n const key = kvMatch.key.trim()\n const rawValue = kvMatch.value.trim()\n\n if (!rawValue) {\n // Possibly a map — next lines will be indented\n currentKey = key\n currentMap = {}\n }\n else {\n frontmatter[key] = unquoteYaml(rawValue)\n }\n }\n\n // Flush any pending map\n if (currentKey && currentMap) {\n frontmatter[currentKey] = currentMap\n }\n\n return { frontmatter, body, diagnostics }\n}\n\nfunction matchFirstColon(line: string): { key: string, value: string } | null {\n const idx = line.indexOf(':')\n if (idx < 0)\n return null\n const key = line.slice(0, idx)\n const value = line.slice(idx + 1)\n // Reject accidental matches where key contains spaces before the colon wider\n // than a normal YAML key (defensive against \"some prose: foo\" in body leak).\n if (!KV_RE.test(`${key}:`))\n return null\n return { key, value }\n}\n\n/**\n * Strip outer quotes and decode YAML-style escapes.\n *\n * - Double-quoted values honor `\\\"`, `\\\\`, `\\n`, `\\r`, `\\t`, `\\b`, `\\f`, `\\/`.\n * - Single-quoted values honor `''` (the YAML single-quote escape for a literal `'`).\n * - Trailing inline comments on unquoted values (` # comment`) are dropped.\n * - Unquoted values are returned as-is (trimmed by caller).\n *\n * Skill frontmatter in the wild uses a narrow YAML subset — this is\n * sufficient for every field in the current spec and the common authoring\n * tools. Block scalars (`|`, `>`) and flow sequences are intentionally left\n * unsupported: they'd only appear in a bespoke workflow, and silently\n * half-supporting them is worse than rejecting them obviously.\n */\nfunction unquoteYaml(val: string): string {\n const dq = val.match(DOUBLE_QUOTED_RE)\n if (dq) {\n return dq[1].replace(DQ_ESCAPE_RE, (_, ch) => {\n switch (ch) {\n case '\"': return '\"'\n case '\\\\': return '\\\\'\n case '/': return '/'\n case 'b': return '\\b'\n case 'f': return '\\f'\n case 'n': return '\\n'\n case 'r': return '\\r'\n case 't': return '\\t'\n default: return ch\n }\n })\n }\n const sq = val.match(SINGLE_QUOTED_RE)\n if (sq) {\n return sq[1].replace(/''/g, '\\'')\n }\n // Strip trailing inline comment only when clearly separated (\"value # note\"\n // is a comment; \"value#tag\" is a literal). Preserves content on values that\n // legitimately contain `#` as part of text.\n const hashIdx = val.indexOf(' #')\n if (hashIdx >= 0)\n return val.slice(0, hashIdx).trimEnd()\n return val\n}\n\n/**\n * Narrow a frontmatter field to a string. Produces a diagnostic when present\n * but not a string, so malformed YAML (e.g. `name: 123`) surfaces as a\n * warning instead of silently coerced downstream via `as string`.\n */\nfunction takeString(\n frontmatter: Record<string, unknown>,\n key: string,\n diagnostics: SkillDiagnostic[],\n): string | undefined {\n const raw = frontmatter[key]\n if (raw === undefined || raw === null)\n return undefined\n if (typeof raw === 'string')\n return raw\n diagnostics.push({\n severity: 'warning',\n code: 'invalid-field-type',\n message: `Frontmatter field \"${key}\" expected string, got ${typeof raw}. Coerced.`,\n field: key,\n })\n return String(raw)\n}\n\n// ---------------------------------------------------------------------------\n// Resource enumeration\n// ---------------------------------------------------------------------------\n\nconst RESOURCE_DIRS: Record<string, SkillResource['type']> = {\n scripts: 'script',\n references: 'reference',\n assets: 'asset',\n}\n\nfunction enumerateResources(baseDir: string): SkillResource[] {\n const resources: SkillResource[] = []\n\n for (const [dir, type] of Object.entries(RESOURCE_DIRS)) {\n const dirPath = join(baseDir, dir)\n if (!existsSync(dirPath) || !statSync(dirPath).isDirectory())\n continue\n try {\n // `readdirSync` with `recursive: true` returns `string | Buffer`; we\n // opt into the `encoding: 'utf8'` overload via `withFileTypes: false`\n // default and string-encoded filenames. Guard the Buffer case in case\n // a platform quirk (non-UTF8 filenames on Linux) slips through.\n const files = readdirSync(dirPath, { recursive: true })\n for (const file of files) {\n const rel = typeof file === 'string' ? file : file.toString('utf-8')\n const fullPath = join(dirPath, rel)\n if (statSync(fullPath).isFile()) {\n resources.push({ path: join(dir, rel), type })\n }\n }\n }\n catch {\n // Skip unreadable directories\n }\n }\n\n // Also enumerate other files in the root (excluding SKILL.md)\n try {\n for (const entry of readdirSync(baseDir)) {\n if (entry === 'SKILL.md')\n continue\n const entryPath = join(baseDir, entry)\n if (statSync(entryPath).isFile()) {\n resources.push({ path: entry, type: 'other' })\n }\n }\n }\n catch {\n // Skip unreadable root\n }\n\n return resources\n}\n\n// ---------------------------------------------------------------------------\n// Parse a single SKILL.md file\n// ---------------------------------------------------------------------------\n\ninterface ParseSkillOptions {\n /** Source tag to attach to the returned SkillConfig. */\n source?: SkillSource\n}\n\n/**\n * Parse a SKILL.md file into a SkillConfig (lenient).\n *\n * Returns `null` only when the skill is fundamentally unusable:\n * - The file is missing\n * - The description is absent (required by spec for disclosure)\n *\n * All other issues are surfaced as `SkillConfig.diagnostics` with severity\n * `warning`. Deprecated top-level fields (`paths`, `model`, `thinking`) are\n * auto-migrated into `metadata['zidane.*']`.\n */\nexport async function parseSkillFile(\n filePath: string,\n options: ParseSkillOptions = {},\n): Promise<SkillConfig | null> {\n const absPath = resolve(filePath)\n if (!existsSync(absPath))\n return null\n\n const content = readFileSync(absPath, 'utf-8')\n const { frontmatter, body, diagnostics } = parseFrontmatter(content)\n\n // Description: frontmatter > first paragraph of body > skip\n let description = takeString(frontmatter, 'description', diagnostics)\n if (!description && body) {\n const firstParagraph = body.split(PARAGRAPH_SPLIT_RE)[0]?.trim()\n if (firstParagraph)\n description = firstParagraph\n }\n if (!description)\n return null\n\n if (description.length > 1024) {\n diagnostics.push({\n severity: 'warning',\n code: 'description-too-long',\n message: `Description exceeds spec limit of 1024 characters (got ${description.length}). Loading anyway.`,\n field: 'description',\n })\n }\n\n const baseDir = dirname(absPath)\n const dirName = basename(baseDir)\n const frontmatterName = takeString(frontmatter, 'name', diagnostics)\n const name = frontmatterName || dirName\n\n // Lenient name checks\n if (frontmatterName && frontmatterName !== dirName) {\n diagnostics.push({\n severity: 'warning',\n code: 'name-mismatch-directory',\n message: `Skill name \"${frontmatterName}\" does not match parent directory \"${dirName}\". Loading anyway.`,\n field: 'name',\n })\n }\n if (name.length > 64) {\n diagnostics.push({\n severity: 'warning',\n code: 'name-too-long',\n message: `Skill name \"${name}\" exceeds spec limit of 64 characters. Loading anyway.`,\n field: 'name',\n })\n }\n if (!validateSkillName(name)) {\n diagnostics.push({\n severity: 'warning',\n code: 'invalid-name-format',\n message: `Skill name \"${name}\" does not match spec format (lowercase alphanumeric + hyphens, no leading/trailing/consecutive hyphens). Loading anyway.`,\n field: 'name',\n })\n }\n\n const config: SkillConfig = {\n name,\n description,\n instructions: body,\n source: options.source ?? 'project',\n location: absPath,\n baseDir,\n resources: enumerateResources(baseDir),\n }\n\n const license = takeString(frontmatter, 'license', diagnostics)\n if (license)\n config.license = license\n\n const compatibility = takeString(frontmatter, 'compatibility', diagnostics)\n if (compatibility) {\n if (compatibility.length > 500) {\n diagnostics.push({\n severity: 'warning',\n code: 'compatibility-too-long',\n message: `Compatibility exceeds spec limit of 500 characters (got ${compatibility.length}). Loading anyway.`,\n field: 'compatibility',\n })\n }\n config.compatibility = compatibility\n }\n\n // Metadata — spec-compliant flat bag\n const metadata: Record<string, string> = {}\n const rawMetadata = frontmatter.metadata\n if (rawMetadata && typeof rawMetadata === 'object' && !Array.isArray(rawMetadata)) {\n for (const [k, v] of Object.entries(rawMetadata as Record<string, unknown>)) {\n if (typeof v !== 'string') {\n diagnostics.push({\n severity: 'warning',\n code: 'invalid-metadata-value',\n message: `Metadata value for \"${k}\" is not a string; coerced. (Spec requires string values.)`,\n field: 'metadata',\n })\n metadata[k] = String(v)\n continue\n }\n metadata[k] = v\n }\n }\n else if (rawMetadata !== undefined) {\n diagnostics.push({\n severity: 'warning',\n code: 'invalid-metadata-shape',\n message: `Frontmatter \"metadata\" expected a map, got ${Array.isArray(rawMetadata) ? 'array' : typeof rawMetadata}. Ignored.`,\n field: 'metadata',\n })\n }\n\n // Auto-migrate deprecated top-level fields into metadata.zidane.*\n const pathsField = takeString(frontmatter, 'paths', diagnostics)\n if (pathsField) {\n metadata['zidane.paths'] = pathsField.split(COMMA_OR_SPACE_RE).filter(Boolean).join(',')\n diagnostics.push({\n severity: 'warning',\n code: 'deprecated-top-level-field',\n message: '`paths` is not a spec field and is deprecated — moved to `metadata[\"zidane.paths\"]`.',\n field: 'paths',\n })\n }\n const modelField = takeString(frontmatter, 'model', diagnostics)\n if (modelField) {\n metadata['zidane.model'] = modelField\n diagnostics.push({\n severity: 'warning',\n code: 'deprecated-top-level-field',\n message: '`model` is not a spec field and is deprecated — moved to `metadata[\"zidane.model\"]`.',\n field: 'model',\n })\n }\n const thinkingField = takeString(frontmatter, 'thinking', diagnostics)\n const effortField = thinkingField ? undefined : takeString(frontmatter, 'effort', diagnostics)\n const legacyThinking = thinkingField ?? effortField\n if (legacyThinking) {\n metadata['zidane.thinking'] = legacyThinking\n diagnostics.push({\n severity: 'warning',\n code: 'deprecated-top-level-field',\n message: `\\`${thinkingField ? 'thinking' : 'effort'}\\` is not a spec field and is deprecated — moved to \\`metadata[\"zidane.thinking\"]\\`.`,\n field: thinkingField ? 'thinking' : 'effort',\n })\n }\n\n if (Object.keys(metadata).length > 0)\n config.metadata = metadata\n\n const allowedTools = takeString(frontmatter, 'allowed-tools', diagnostics)\n if (allowedTools) {\n config.allowedTools = allowedTools.split(WHITESPACE_SPLIT_RE).filter(Boolean)\n }\n\n if (diagnostics.length > 0)\n config.diagnostics = diagnostics\n\n return config\n}\n\n// ---------------------------------------------------------------------------\n// Directory scanning\n// ---------------------------------------------------------------------------\n\nconst SKIP_DIRS = new Set(['.git', 'node_modules', '.DS_Store', 'dist', 'build'])\n\nfunction findSkillDirs(root: string, maxDepth = 4, _depth = 0): string[] {\n if (_depth > maxDepth)\n return []\n if (!existsSync(root) || !statSync(root).isDirectory())\n return []\n\n const results: string[] = []\n try {\n for (const entry of readdirSync(root)) {\n if (SKIP_DIRS.has(entry))\n continue\n const entryPath = join(root, entry)\n if (!statSync(entryPath).isDirectory())\n continue\n\n const skillFile = join(entryPath, 'SKILL.md')\n if (existsSync(skillFile) && statSync(skillFile).isFile()) {\n results.push(skillFile)\n }\n else {\n // Recurse deeper\n results.push(...findSkillDirs(entryPath, maxDepth, _depth + 1))\n }\n }\n }\n catch {\n // Skip unreadable directories\n }\n return results\n}\n\n// ---------------------------------------------------------------------------\n// Default scan paths\n// ---------------------------------------------------------------------------\n\n/** A scan path paired with the source tag that should be attached to any skills found in it. */\nexport interface SourcedScanPath {\n path: string\n source: SkillSource\n}\n\n/**\n * Return the default scan paths tagged by source. Project-scope paths come\n * first; their skills therefore win on name collisions against user-scope\n * skills (first-found wins in discovery).\n */\nexport function getDefaultScanPaths(): SourcedScanPath[] {\n const home = homedir()\n const cwd = process.cwd()\n return [\n // Project-level (higher priority)\n { path: join(cwd, '.agents', 'skills'), source: 'project' },\n { path: join(cwd, '.zidane', 'skills'), source: 'project' },\n // User-level (lower priority)\n { path: join(home, '.agents', 'skills'), source: 'user' },\n { path: join(home, '.zidane', 'skills'), source: 'user' },\n ]\n}\n\n/**\n * Infer a source tag for a user-provided scan path.\n * Paths under `$HOME` are treated as 'user'; everything else as 'project'.\n */\nexport function inferSource(path: string): SkillSource {\n return path.startsWith(homedir()) ? 'user' : 'project'\n}\n\n// ---------------------------------------------------------------------------\n// Discover skills from filesystem\n// ---------------------------------------------------------------------------\n\n/**\n * Discover skills from sourced filesystem paths.\n * Each path is scanned for subdirectories containing SKILL.md.\n * Earlier paths have higher priority (first-found wins on name collision).\n */\nexport async function discoverSkills(paths: SourcedScanPath[]): Promise<SkillConfig[]> {\n const skillsByName = new Map<string, SkillConfig>()\n\n for (const { path: scanPath, source } of paths) {\n const skillFiles = findSkillDirs(resolve(scanPath))\n for (const file of skillFiles) {\n const skill = await parseSkillFile(file, { source })\n if (!skill)\n continue\n if (skillsByName.has(skill.name)) {\n // First-found wins — append a collision diagnostic to the winning skill.\n const existing = skillsByName.get(skill.name)!\n const diag: SkillDiagnostic = {\n severity: 'warning',\n code: 'name-collision-shadowed',\n message: `A skill with name \"${skill.name}\" was also found at ${file} (source: ${source}); shadowed by ${existing.location} (source: ${existing.source}).`,\n }\n existing.diagnostics = [...(existing.diagnostics ?? []), diag]\n continue\n }\n skillsByName.set(skill.name, skill)\n }\n }\n\n return [...skillsByName.values()]\n}\n","/**\n * Skill writer — materializes inline SkillConfig objects to disk as proper\n * SKILL.md files so they participate fully in the filesystem-based skill system.\n *\n * Strict-validates each skill before writing (see `validateSkillForWrite`).\n */\n\nimport type { SkillConfig } from './types'\nimport { mkdirSync, writeFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { validateSkillForWrite } from './validate'\n\n// ---------------------------------------------------------------------------\n// Frontmatter serialization\n// ---------------------------------------------------------------------------\n\nconst YAML_RESERVED_RE = /[:#&*!|>%@`]/\nconst YAML_EDGE_OR_QUOTE_RE = /^\\s|\\s$|[\"']/\nconst DQUOTE_RE = /\"/g\nconst LEADING_NEWLINES_RE = /^\\n+/\n\nfunction yamlEscape(value: string): string {\n // Quote values that contain YAML-reserved characters (colons, quotes, leading\n // special chars). Keeps round-trip safety for descriptions like\n // \"Use when: the user asks …\".\n if (YAML_RESERVED_RE.test(value) || YAML_EDGE_OR_QUOTE_RE.test(value) || value === '')\n return `\"${value.replace(DQUOTE_RE, '\\\\\"')}\"`\n return value\n}\n\nfunction serializeFrontmatter(skill: SkillConfig): string {\n const lines: string[] = ['---']\n\n lines.push(`name: ${yamlEscape(skill.name)}`)\n lines.push(`description: ${yamlEscape(skill.description)}`)\n\n if (skill.license)\n lines.push(`license: ${yamlEscape(skill.license)}`)\n if (skill.compatibility)\n lines.push(`compatibility: ${yamlEscape(skill.compatibility)}`)\n if (skill.allowedTools?.length)\n lines.push(`allowed-tools: ${skill.allowedTools.join(' ')}`)\n\n if (skill.metadata && Object.keys(skill.metadata).length > 0) {\n lines.push('metadata:')\n for (const [key, value] of Object.entries(skill.metadata)) {\n // Always quote metadata values — preserves string-looking numerics (\"1.0\")\n // and string-valued booleans (\"true\") from YAML's implicit type coercion.\n // Dot-keys (e.g. `zidane.paths`) are accepted verbatim by YAML.\n lines.push(` ${key}: \"${value.replace(DQUOTE_RE, '\\\\\"')}\"`)\n }\n }\n\n lines.push('---')\n return lines.join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Write a single skill to disk\n// ---------------------------------------------------------------------------\n\n/**\n * Write a `SkillConfig` to disk as a proper skill directory with SKILL.md.\n * Returns the path to the written SKILL.md file.\n *\n * Throws if the skill fails `validateSkillForWrite` — the authoring path is\n * strict on purpose. For loading third-party skills, use `parseSkillFile`\n * directly (lenient).\n */\nexport function writeSkillToDisk(skill: SkillConfig, targetDir: string): string {\n const result = validateSkillForWrite(skill)\n if (!result.valid) {\n const summary = result.errors\n .map(e => ` - [${e.code}] ${e.message}`)\n .join('\\n')\n throw new Error(`Cannot write invalid skill \"${skill.name}\":\\n${summary}`)\n }\n\n const skillDir = join(targetDir, skill.name)\n mkdirSync(skillDir, { recursive: true })\n\n const frontmatter = serializeFrontmatter(skill)\n // Normalize: strip any leading newlines on the body so we never emit a\n // double-blank-line between the frontmatter fence and the instructions,\n // and always end the file with a single trailing newline.\n const bodyTrimmed = skill.instructions ? skill.instructions.replace(LEADING_NEWLINES_RE, '') : ''\n const content = bodyTrimmed\n ? `${frontmatter}\\n\\n${bodyTrimmed}\\n`\n : `${frontmatter}\\n`\n\n const skillPath = join(skillDir, 'SKILL.md')\n writeFileSync(skillPath, content)\n\n return skillPath\n}\n\n// ---------------------------------------------------------------------------\n// Write multiple skills to a directory\n// ---------------------------------------------------------------------------\n\n/**\n * Write multiple `SkillConfig` objects to a target directory.\n * Each skill gets its own subdirectory with a SKILL.md file.\n * Returns the target directory path (for use as a scan path).\n */\nexport function writeSkillsToDisk(skills: SkillConfig[], targetDir: string): string {\n mkdirSync(targetDir, { recursive: true })\n for (const skill of skills) {\n writeSkillToDisk(skill, targetDir)\n }\n return targetDir\n}\n","/**\n * Skill resolution — discovers filesystem skills, writes dynamic skills,\n * merges everything, and applies filtering.\n */\n\nimport type { SourcedScanPath } from './discovery'\nimport type { SkillConfig, SkillsConfig } from './types'\nimport { mkdtempSync, rmSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport { discoverSkills, getDefaultScanPaths, inferSource } from './discovery'\nimport { writeSkillsToDisk } from './writer'\n\n// ---------------------------------------------------------------------------\n// Resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Resolved-skills bundle: the materialized list plus a `cleanup` fn that\n * removes any temporary directory created for `config.write` skills.\n *\n * Inline skills must live on disk for the lifetime of the agent (the\n * `skills_read` / `skills_run_script` tools resolve relative paths against\n * `baseDir`), so cleanup is deferred to `agent.destroy()`. When no temp\n * directory was created, `cleanup` is a no-op.\n */\nexport interface ResolvedSkillsBundle {\n skills: SkillConfig[]\n cleanup: () => void\n}\n\n/**\n * Resolve all skills from a SkillsConfig:\n *\n * 1. Materialize `config.write` entries to a temp directory, tagged as `inline`\n * 2. Combine with default + user-provided scan paths (project-first, user-next)\n * 3. Run lenient discovery\n * 4. Apply filters: `exclude`, `enabled` allowlist, optional project-skill trust gate\n *\n * Returns `{ skills, cleanup }` — call `cleanup()` on agent destroy to remove\n * the temp directory created for inline `config.write` skills. The agent\n * factory wires this automatically; standalone callers must invoke it\n * themselves to avoid leaking OS temp.\n */\nexport async function resolveSkills(config: SkillsConfig): Promise<ResolvedSkillsBundle> {\n const sourcedPaths: SourcedScanPath[] = []\n let writeDir: string | undefined\n\n if (!config.skipDefaultPaths) {\n sourcedPaths.push(...getDefaultScanPaths())\n }\n\n // User-provided scan paths: infer source from path prefix.\n for (const p of config.scan ?? []) {\n sourcedPaths.push({ path: p, source: inferSource(p) })\n }\n\n // Inline skills materialized to a temp directory and scanned as 'inline'.\n if (config.write?.length) {\n writeDir = mkdtempSync(join(tmpdir(), 'zidane-skills-'))\n writeSkillsToDisk(config.write, writeDir)\n sourcedPaths.push({ path: writeDir, source: 'inline' })\n }\n\n // Discover all skills from filesystem (first-found-wins on name collision).\n let skills = await discoverSkills(sourcedPaths)\n\n // Optional trust gate: drop project-scoped skills when the host hasn't\n // marked the project as trusted. Undefined source is treated as `'project'`\n // (conservative default — an untrusted environment should skip unclassified\n // skills rather than silently loading them).\n if (config.trustProjectSkills === false) {\n skills = skills.filter(s => s.source !== undefined && s.source !== 'project')\n }\n\n // Filter out `exclude`\n const exclude = new Set(config.exclude ?? [])\n let filtered = skills.filter(s => !exclude.has(s.name))\n\n // Apply `enabled` allowlist when it's an array of skill names\n if (Array.isArray(config.enabled)) {\n const allowlist = new Set(config.enabled)\n filtered = filtered.filter(s => allowlist.has(s.name))\n }\n\n const cleanup = writeDir\n ? () => {\n try {\n rmSync(writeDir!, { recursive: true, force: true })\n }\n catch {\n // Best-effort — temp directory may have been removed by the OS or\n // a sibling cleanup. Don't crash agent destroy.\n }\n }\n : () => {}\n\n return { skills: filtered, cleanup }\n}\n","/**\n * Shell interpolation for skill instructions.\n *\n * The `!\\`command\\`` syntax runs shell commands as a preprocessing step\n * before skill content is sent to the agent. The command output replaces\n * the placeholder — the agent only sees the final result.\n *\n * Example:\n * - PR diff: !\\`gh pr diff\\`\n * → - PR diff: <actual diff output>\n */\n\nimport type { ExecutionContext, ExecutionHandle } from '../contexts'\n\n/** Regex to match !`command` patterns in skill instructions */\nconst SHELL_INTERPOLATION_RE = /!`([^`]+)`/g\n\n/**\n * Interpolate shell commands in skill instructions.\n *\n * Runs each !\\`command\\` through the execution context and replaces\n * the placeholder with the command's stdout. If a command fails,\n * the placeholder is replaced with an error message.\n *\n * @param instructions - Raw skill instructions with potential !\\`command\\` patterns\n * @param execution - The execution context to run commands in\n * @param handle - The active execution handle\n * @returns Instructions with all !\\`command\\` patterns replaced by output\n */\nexport async function interpolateShellCommands(\n instructions: string,\n execution: ExecutionContext,\n handle: ExecutionHandle,\n): Promise<string> {\n const matches = [...instructions.matchAll(SHELL_INTERPOLATION_RE)]\n if (matches.length === 0)\n return instructions\n\n // Collect all commands with their positions for precise replacement\n const replacements: { index: number, length: number, output: string }[] = []\n\n for (const match of matches) {\n const command = match[1]\n const index = match.index!\n const length = match[0].length\n\n try {\n const result = await execution.exec(handle, command, { timeout: 30 })\n const output = result.exitCode === 0\n ? result.stdout.trim()\n : `[command failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}]`\n replacements.push({ index, length, output })\n }\n catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n replacements.push({ index, length, output: `[command error: ${message}]` })\n }\n }\n\n // Apply replacements in reverse order to preserve positions\n let result = instructions\n for (let i = replacements.length - 1; i >= 0; i--) {\n const { index, length, output } = replacements[i]\n result = result.slice(0, index) + output + result.slice(index + length)\n }\n\n return result\n}\n"],"mappings":"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"}