@noir-ai/skills 1.11.2-beta.1 → 1.12.0-beta.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.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { ConflictResolution } from '@noir-ai/core';
1
2
  import * as z from 'zod';
2
3
 
3
4
  interface SkillFrontmatter {
@@ -123,12 +124,11 @@ interface EmitSummary {
123
124
  conflicts?: SkillConflict[];
124
125
  }
125
126
  /**
126
- * Resolution choice for a skill-file conflict. Mirrors
127
- * @noir-ai/create's `ConflictResolution` literally so the CLI's
128
- * `buildConflictOpts().onConflict` is structurally compatible, WITHOUT the
129
- * skills package gaining a create dependency.
127
+ * Resolution choice for a skill-file conflict. Re-exports the shared
128
+ * `ConflictResolution` union from @noir-ai/core (no create dependency) so the
129
+ * CLI's `buildConflictOpts().onConflict` is structurally compatible.
130
130
  */
131
- type SkillConflictResolution = 'replace' | 'preserve' | 'rename' | 'duplicate' | 'cancel';
131
+ type SkillConflictResolution = ConflictResolution;
132
132
  /**
133
133
  * Context passed to {@link SkillConflictResolver}. Mirrors
134
134
  * @noir-ai/create's `ConflictContext` (relPath/existing/proposed/mode) so the
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/compiler.ts
2
- import { createHash } from "crypto";
3
2
  import { mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
4
3
  import { basename, dirname as dirname2, join as join2 } from "path";
4
+ import { sha256Hex12, uniqueAsideSync } from "@noir-ai/core";
5
5
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
6
6
 
7
7
  // src/discover.ts
@@ -496,9 +496,6 @@ async function isUserOwnedCursorLegacyDir(dirAbs) {
496
496
  return true;
497
497
  }
498
498
  }
499
- function sha256Hex12(s) {
500
- return createHash("sha256").update(s, "utf8").digest("hex").slice(0, 12);
501
- }
502
499
  function similarity(a, b) {
503
500
  if (a === b) return 1;
504
501
  const A = a.split("\n");
@@ -521,17 +518,11 @@ function similarity(a, b) {
521
518
  }
522
519
  async function renameAside(abs, suffix) {
523
520
  const { rename } = await import("fs/promises");
524
- const dest = await uniqueAsideAbs(abs, suffix);
521
+ const dest = await uniqueAside(abs, suffix);
525
522
  await rename(abs, dest);
526
523
  }
527
524
  async function uniqueAside(abs, suffix) {
528
- return uniqueAsideAbs(abs, suffix);
529
- }
530
- async function uniqueAsideAbs(abs, suffix) {
531
- const { existsSync } = await import("fs");
532
- let candidate = `${abs}${suffix}`;
533
- for (let n = 1; existsSync(candidate); n++) candidate = `${abs}${suffix}.${n}`;
534
- return candidate;
525
+ return uniqueAsideSync(abs, suffix);
535
526
  }
536
527
 
537
528
  // src/evals.ts
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/compiler.ts","../src/discover.ts","../src/integrations-schema.ts","../src/quality.ts","../src/evals.ts","../src/registry.ts","../src/residue.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { discoverAll, discoverBuiltin } from './discover.js';\nimport { runtimeEmitsHostMcp } from './integrations-schema.js';\nimport {\n artifactPathDrift,\n chainedReferences,\n isWhatWhenDescription,\n lintWarnings,\n looksLikeWhenDescription,\n MAX_BODY_LINES,\n missingSections,\n withinLineBudget,\n} from './quality.js';\n\n// Re-exported for backward-compat — callers (hygiene tests, CLI) import\n// `looksLikeWhenDescription` from './compiler.js'. The single source of truth\n// is quality.ts; this alias keeps the old import surface stable.\nexport { looksLikeWhenDescription } from './quality.js';\n\nimport type {\n BuiltinSkill,\n CompiledIntegration,\n CompiledSkill,\n CompileTarget,\n EmitSummary,\n IntegrationSkill,\n SkillConflict,\n SkillConflictContext,\n SkillConflictResolution,\n SkillConflictResolver,\n SkillFrontmatter,\n ValidationResult,\n} from './types.js';\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/;\nconst NAME_RE = /^noir-[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst MAX_DESC = 1024;\n\nexport function parseFrontmatter(md: string): SkillFrontmatter {\n const m = md.match(FRONTMATTER_RE);\n if (!m) throw new Error('Skill missing YAML frontmatter (expected --- ... ---)');\n // Group 1 always exists when the regex matches; guard past noUncheckedIndexedAccess.\n const yaml = m[1];\n if (yaml === undefined) throw new Error('Skill missing YAML frontmatter (expected --- ... ---)');\n const fm = parseYaml(yaml) as SkillFrontmatter;\n if (typeof fm?.name !== 'string' || typeof fm?.description !== 'string') {\n throw new Error('Skill frontmatter requires string `name` + `description`');\n }\n return fm;\n}\n\nexport function bodyOf(md: string): string {\n return md.replace(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?/, '');\n}\n\nexport function validateSkill(skill: BuiltinSkill): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const { name, description, metadata } = skill.frontmatter;\n if (!name) errors.push('missing `name`');\n else if (!NAME_RE.test(name)) errors.push(`name \"${name}\" must match noir-<kebab>`);\n if (basename(skill.dir) !== name) {\n errors.push(`dir \"${basename(skill.dir)}\" must equal name \"${name}\"`);\n }\n if (!description?.trim()) errors.push('missing `description`');\n else if (description.length > MAX_DESC) errors.push(`description exceeds ${MAX_DESC} chars`);\n else if (!looksLikeWhenDescription(description)) {\n errors.push('description must state WHEN to trigger (e.g. \"Use when…\"), not WHAT it does');\n } else if (!isWhatWhenDescription(description)) {\n // C3: the description must ALSO carry a WHAT clause — the trigger phrase\n // alone (WHEN-only) fails. Errors, not warnings: it's part of the contract.\n errors.push('description must be WHAT+WHEN — a WHAT clause after the trigger phrase');\n }\n // C3 structural gate: metadata, required sections, line budget, one-level refs.\n if (!metadata?.category?.trim()) errors.push('missing `metadata.category`');\n if (!metadata?.version?.trim()) errors.push('missing `metadata.version`');\n const body = bodyOf(skill.skillMd);\n const missing = missingSections(body);\n for (const sec of missing) errors.push(`missing required section: ${sec}`);\n if (!withinLineBudget(body)) {\n errors.push(`body exceeds ${MAX_BODY_LINES}-line budget (${body.split('\\n').length} lines)`);\n }\n const chained = chainedReferences(skill);\n for (const r of chained)\n errors.push(`reference \"${r}\" chains to another reference (must be one level deep)`);\n for (const r of skill.references) {\n if (!/^[a-z0-9-]+\\.md$/i.test(r.name)) errors.push(`reference \"${r.name}\" must be <kebab>.md`);\n if (!r.content.trim()) errors.push(`reference \"${r.name}\" is empty`);\n }\n // C3 generated-artifact standard: no `.noir/` output-path drift in the body\n // or references (unknown dir, or a filename missing its type-code prefix).\n for (const d of artifactPathDrift(skill)) errors.push(d);\n // Soft warnings (lint-level) — advisory, non-failing.\n warnings.push(...lintWarnings(skill));\n return { ok: errors.length === 0, errors, warnings: warnings.length > 0 ? warnings : undefined };\n}\n\n/**\n * `lintSkill` — the C3 soft quality gate. Errors = `validateSkill` errors (a\n * skill that fails validation is broken); warnings = `quality.ts` style rules\n * (thin body, no examples, first-person narration, …). A skill can validate\n * clean yet still carry lint warnings the author should resolve.\n */\nexport function lintSkill(skill: BuiltinSkill): {\n name: string;\n errors: string[];\n warnings: string[];\n} {\n const res = validateSkill(skill);\n return { name: skill.name, errors: res.errors, warnings: res.warnings ?? [] };\n}\n\n/**\n * Compile a builtin skill into the host-shaped `CompiledSkill`. The validator\n * runs for every target (a malformed skill is malformed in any format).\n *\n * - `claude` | `agents-md` | `gemini` | `opencode` (the AGENTS.md-aligned\n * hosts): canonical format copied verbatim — `SKILL.md` plus its\n * `references/` siblings. These hosts read the same SKILL.md shape;\n * the EMIT-LOCATION (which dir they land in) is the cli/adapter's job, not\n * the compiler's.\n * - `cursor`: transform into a Cursor `.mdc` rule — ONE file `<name>.mdc`\n * whose frontmatter carries the skill description, a wildcard globs entry,\n * and `alwaysApply: false` (the agent decides whether to pull the rule via\n * the description; never auto-applied). The body is the SKILL.md BODY\n * (frontmatter stripped). Cursor's rule format has no references concept,\n * so references are dropped (documented in the S10 spec risks — the body is\n * the surface).\n *\n * The `target` defaults to `'claude'` for backward compatibility with every\n * existing caller; the verbatim branch produces byte-identical output to v1.1\n * (the regression anchor).\n */\nexport function compileSkill(skill: BuiltinSkill, target: CompileTarget = 'claude'): CompiledSkill {\n const res = validateSkill(skill);\n if (!res.ok) throw new Error(`Cannot compile ${skill.name}: ${res.errors.join('; ')}`);\n if (target === 'cursor') {\n return { name: skill.name, files: [{ path: [`${skill.name}.mdc`], content: toMdc(skill) }] };\n }\n // claude / agents-md / gemini / opencode → canonical verbatim format.\n const files = [\n { path: ['SKILL.md'], content: skill.skillMd },\n ...skill.references.map((r) => ({ path: ['references', r.name], content: r.content })),\n ];\n return { name: skill.name, files };\n}\n\n/**\n * Cursor `.mdc` rule transform — frontmatter `{description, globs, alwaysApply:\n * false}` + the SKILL.md body. The description drives Cursor's agent-decided\n * rule selection (per the S10 spec open-decision default: `alwaysApply: false`).\n * Cursor's rule format has no references/ concept, so the body alone is the\n * surface; references are dropped (documented risk in the spec).\n *\n * Frontmatter is YAML-serialized via the `yaml` package (already a dep) so\n * description escaping (colons, quotes, multi-line) is correct without hand\n * rolling. `lineWidth: 0` keeps descriptions on a single quoted line (predictable\n * byte shape; Cursor tolerates either). */\nfunction toMdc(skill: BuiltinSkill): string {\n const frontmatter = stringifyYaml(\n {\n description: skill.frontmatter.description,\n globs: ['**/*'],\n alwaysApply: false,\n },\n { lineWidth: 0 },\n ).trimEnd();\n const body = bodyOf(skill.skillMd);\n // Cursor's `.mdc` shape: leading `---\\n`, frontmatter, closing `---\\n`, blank\n // line, body. Trailing newline so files concatenate cleanly on disk.\n return `---\\n${frontmatter}\\n---\\n${body.endsWith('\\n') ? body : `${body}\\n`}`;\n}\n\n/**\n * Compile an integration: validates the SKILL.md (same shape as builtins) +\n * renders the same per-skill files. When the integration's `runtime` widens\n * emission (`mcp-stdio`/`external-mcp`) AND it carries a non-null `mcp`\n * declaration, also expose a provider-neutral `hostMcp` server block the host\n * adapter may merge into the host MCP config (e.g. Claude's `.mcp.json`).\n *\n * For `gated-write-proxy` (ClickUp) + `none` → no `hostMcp` (writes route\n * through Noir's own MCP tool; the host MCP config is unchanged). For\n * `mcp-stdio` the hostMcp is exposed (the compiler widens emission); the\n * Claude adapter still does not emit a NEW server entry for it (Noir's own\n * `noir mcp serve` entry already covers it).\n */\nexport function compileIntegration(\n integration: IntegrationSkill,\n target: CompileTarget = 'claude',\n): CompiledIntegration {\n // Validate the skill half first (throws on a bad SKILL.md — same path as\n // builtins). compileSkill returns a base CompiledSkill; we layer hostMcp on.\n const base = compileSkill(integration, target);\n const { runtime, mcp } = integration.declaration;\n if (runtimeEmitsHostMcp(runtime) && mcp !== null) {\n return {\n ...base,\n hostMcp: {\n serverName: integration.name,\n command: mcp.command,\n args: mcp.args,\n transport: mcp.transport,\n url: mcp.url,\n env: mcp.env,\n },\n };\n }\n return base;\n}\n\nexport async function emitSkillsToDir(\n targetDir: string,\n opts: {\n builtinDir?: string;\n integrationsDir?: string;\n includeIntegrations?: boolean;\n /** S10 CompileTarget — selects the per-skill transform. Defaults to\n * `'claude'` (the v1.1 verbatim SKILL.md shape) so every existing caller\n * stays byte-identical. `'cursor'` compiles each skill to a `.mdc` rule\n * file (frontmatter `description`/`globs`/`alwaysApply:false` + the\n * SKILL.md body); the other targets map to the verbatim shape (their\n * hosts have no skill concept, so emit is skipped upstream — but the\n * default-verbatim policy keeps the signature total). */\n target?: CompileTarget;\n /** Policy for a skill file that exists AND differs from the compiled\n * bytes, when no {@link onConflict} resolver is wired (or non-interactive).\n * Default `'overwrite'` preserves the v1.2 behavior (every sync clobbers).\n * `'preserve'` skips the differing file (the CI / non-TTY default the CLI\n * threads via `buildConflictOpts`). */\n conflictPolicy?: 'overwrite' | 'preserve';\n /** Per-file conflict resolver. The CLI passes its single\n * `buildConflictOpts().onConflict` (clack menu + diff preview + apply-to-\n * all) through unchanged; structurally compatible with @noir-ai/create's\n * `ConflictResolverReturn`. */\n onConflict?: SkillConflictResolver;\n /** Explicit interactivity flag (the engine reads THIS, not process.env,\n * matching @noir-ai/create's hermetic-interactive contract). When `false`,\n * the resolver is NEVER consulted (CI / --json never hangs on a prompt). */\n interactive?: boolean;\n } = {},\n): Promise<EmitSummary> {\n const target: CompileTarget = opts.target ?? 'claude';\n const { builtins, integrations } = discoverAll({\n builtinDir: opts.builtinDir,\n integrationsDir: opts.integrationsDir,\n });\n const emitIntegrations = opts.includeIntegrations ?? true;\n // Validate the whole pack before writing anything (fail-fast, atomic-ish).\n for (const s of builtins) {\n const res = validateSkill(s);\n if (!res.ok) throw new Error(`Invalid builtin skill ${s.name}: ${res.errors.join('; ')}`);\n }\n if (emitIntegrations) {\n for (const i of integrations) {\n const res = validateSkill(i);\n if (!res.ok) throw new Error(`Invalid integration ${i.name}: ${res.errors.join('; ')}`);\n }\n }\n await mkdir(targetDir, { recursive: true });\n let references = 0;\n const emitted: string[] = [];\n const integrationNames: string[] = [];\n const conflicts: SkillConflict[] = [];\n // Apply-to-all memory keyed by artifact CLASS. Skill emission shares one\n // decision across the run (a `noir sync` touching 8 skill files → 1 prompt)\n // when the resolver returns `{resolution, applyToAll: true}`.\n const memory = new Map<string, SkillConflictResolution>();\n const policy: 'overwrite' | 'preserve' = opts.conflictPolicy ?? 'overwrite';\n // Non-interactive guard. The bridge `NOIR_NON_INTERACTIVE` is set by the\n // bin's preAction under --json/--no-input; the explicit `interactive: false`\n // wins over the env (mirrors @noir-ai/create's hermetic flag). When false,\n // the resolver is NEVER consulted — differing files fall back to `policy`.\n const interactive =\n opts.interactive ??\n (process.env.NOIR_NON_INTERACTIVE === undefined || process.env.NOIR_NON_INTERACTIVE === '');\n\n /** Write a compiled skill file through the SAME conflict seam as\n * `regenerate`: read existing; if differs, consult the resolver (when\n * interactive + wired); record the conflict for the structured report. */\n const writeWithConflict = async (\n absDest: string,\n relDest: string,\n content: string,\n isReference: boolean,\n ): Promise<void> => {\n let existing: string | undefined;\n try {\n existing = await readFile(absDest, 'utf8');\n } catch {\n existing = undefined;\n }\n if (existing !== undefined && existing !== content) {\n const MODE = 'skill' as const;\n let resolution: SkillConflictResolution;\n const remembered = memory.get(MODE);\n if (remembered !== undefined) {\n resolution = remembered;\n } else if (interactive && opts.onConflict !== undefined) {\n const ctx: SkillConflictContext = {\n relPath: relDest,\n existing,\n proposed: content,\n mode: MODE,\n };\n const ret = await opts.onConflict(ctx);\n const unwrapped: SkillConflictResolution = typeof ret === 'string' ? ret : ret.resolution;\n if (typeof ret !== 'string' && ret.applyToAll === true) {\n memory.set(MODE, unwrapped);\n }\n resolution = unwrapped;\n } else {\n resolution = policy === 'preserve' ? 'preserve' : 'replace';\n }\n conflicts.push({\n path: relDest,\n mode: MODE,\n similarity: similarity(existing, content),\n existingSha: sha256Hex12(existing),\n proposedSha: sha256Hex12(content),\n resolution,\n });\n if (resolution === 'cancel') {\n throw new Error(`skill emit cancelled by user at ${relDest}`);\n }\n if (resolution === 'preserve') {\n // Skip this file; the user's bytes stand. Do not count as a reference.\n return;\n }\n if (resolution === 'rename') {\n // Move the user's file aside (unique suffix), then write the compiled bytes.\n await renameAside(absDest, '.local');\n } else if (resolution === 'duplicate') {\n // Write the compiled bytes to <absDest>.noir; keep the user's file.\n await writeFile(await uniqueAside(absDest, '.noir'), content, 'utf8');\n return;\n }\n // 'replace' → fall through and overwrite.\n }\n await writeFile(absDest, content, 'utf8');\n if (isReference) references++;\n };\n\n // Cursor skills land FLAT under targetDir (`.cursor/rules/<name>.mdc`) —\n // Cursor's rule loader scans `.cursor/rules/*.mdc` and does NOT recurse into\n // per-name subdirs. The verbatim branch (claude/agents-md/gemini/opencode)\n // keeps the canonical nested layout (`<name>/SKILL.md` + `<name>/references/`).\n const flat = target === 'cursor';\n for (const s of builtins) {\n const compiled = compileSkill(s, target);\n for (const f of compiled.files) {\n const dest = flat ? join(targetDir, ...f.path) : join(targetDir, s.name, ...f.path);\n await mkdir(dirname(dest), { recursive: true });\n const rel = flat ? f.path.join('/') : [s.name, ...f.path].join('/');\n await writeWithConflict(dest, rel, f.content, f.path[0] !== 'SKILL.md');\n }\n emitted.push(s.name);\n }\n if (emitIntegrations) {\n for (const i of integrations) {\n const compiled = compileIntegration(i, target);\n for (const f of compiled.files) {\n const dest = flat ? join(targetDir, ...f.path) : join(targetDir, i.name, ...f.path);\n await mkdir(dirname(dest), { recursive: true });\n const rel = flat ? f.path.join('/') : [i.name, ...f.path].join('/');\n await writeWithConflict(dest, rel, f.content, f.path[0] !== 'SKILL.md');\n }\n // `hostMcp` is NOT written per-skill — it is surfaced to the host adapter\n // (via discoverAll/compileIntegration) to merge into the host's single\n // MCP config (e.g. `.mcp.json`). Wiring lives in cli/daemon.\n emitted.push(i.name);\n integrationNames.push(i.name);\n }\n }\n\n // --- Prune stale `noir-*` entries from the managed namespace ----------\n // Idempotent hygiene: a previous Noir version may have shipped a builtin that\n // was since renamed/removed (e.g. `noir-old-thing`). Each `noir sync`\n // re-writes the CURRENT pack but a stale entry would otherwise linger forever.\n // After emit, scan `targetDir` for `noir-`-prefixed entries NOT in the emitted\n // set and remove them. ONLY the `noir-` namespace — user skills without the\n // prefix are NEVER touched (they are not Noir's to manage).\n //\n // `assertNotUserOwned` guard: a `noir-*` entry whose content does NOT\n // match the canonical Noir-emitted shape (SKILL.md with `name: noir-…`\n // frontmatter, OR a `.mdc` with the cursor frontmatter) is treated as\n // USER-AUTHORED and LEFT ALONE. A user hand-rolling `noir-myown/SKILL.md`\n // must never be silently deleted by a sync.\n //\n // Shape awareness: the nested layout (claude/agents-md/gemini/opencode)\n // writes one `noir-<name>/` DIR per skill → prune stale DIRS. The cursor flat\n // layout writes one `noir-<name>.mdc` FILE per skill → prune stale .mdc FILES.\n // Cursor ALSO clears legacy `noir-<name>/` dirs (nesting residue) so an\n // upgrade from nested→flat does not leave orphans under `.cursor/rules/`.\n const keep = new Set(emitted);\n const pruned: string[] = [];\n const preservedUserOwned: string[] = [];\n let dirEntries: import('node:fs').Dirent[] = [];\n try {\n dirEntries = await readdir(targetDir, { withFileTypes: true });\n } catch {\n dirEntries = []; // targetDir vanished between the mkdir above and now — nothing to prune.\n }\n for (const ent of dirEntries) {\n if (!ent.name.startsWith('noir-')) continue;\n if (flat) {\n // Cursor flat layout — prune stale FILES (`noir-<name>.mdc`) + legacy\n // nested DIRS (`noir-<name>/`) from a sync.\n if (ent.isFile()) {\n const mdcMatch = ent.name.match(/^(noir-[a-z0-9]+(?:-[a-z0-9]+)*)\\.mdc$/);\n const skillName = mdcMatch?.[1];\n if (skillName && keep.has(skillName)) continue;\n // `assertNotUserOwned`: a `.mdc` without canonical cursor\n // frontmatter (globs + alwaysApply) was hand-rolled by the user; skip.\n if (await isUserOwnedMdc(join(targetDir, ent.name))) {\n preservedUserOwned.push(ent.name);\n continue;\n }\n } else if (ent.isDirectory()) {\n // Legacy cursor nested dir — clear unconditionally (the flat\n // layout has NO `noir-{name}/` dirs under .cursor/rules/; any such dir\n // is stale by definition, regardless of name overlap with the current\n // pack — the fresh `.mdc` file is what's kept, not the dir).\n // Guard: skip if NONE of the `.mdc` files inside look Noir-emitted\n // (canonical cursor frontmatter) — a user hand-rolling a noir-X/ dir\n // with arbitrary content is preserved.\n if (await isUserOwnedCursorLegacyDir(join(targetDir, ent.name))) {\n preservedUserOwned.push(ent.name);\n continue;\n }\n } else {\n continue;\n }\n } else {\n // Nested layout — prune stale DIRS only (the canonical shape).\n if (!ent.isDirectory()) continue;\n if (keep.has(ent.name)) continue;\n // `assertNotUserOwned`: a `noir-*/` dir whose SKILL.md does not\n // carry canonical Noir frontmatter (`name: noir-…`) is user-authored.\n if (await isUserOwnedSkillDir(join(targetDir, ent.name))) {\n preservedUserOwned.push(ent.name);\n continue;\n }\n }\n // Best-effort removal: a failure to remove a stale entry must NOT fail the\n // emit (the fresh skills are already on disk and valid). Surface it via the\n // summary so a caller can warn; the next sync will try again.\n try {\n await rm(join(targetDir, ent.name), { recursive: true, force: true });\n pruned.push(ent.name);\n } catch {\n // Swallow — stale-entry pruning is hygienic, not correctness-critical.\n }\n }\n\n return {\n dir: targetDir,\n emitted,\n references,\n integrations: integrationNames,\n pruned,\n ...(preservedUserOwned.length > 0 ? { preservedUserOwned } : {}),\n ...(conflicts.length > 0 ? { conflicts } : {}),\n };\n}\n\n/** `assertNotUserOwned` for the nested layout: a `noir-{name}/` dir is\n * Noir-managed iff it contains a SKILL.md whose YAML frontmatter carries a\n * `name:` matching `noir-<dir>`. Missing SKILL.md, missing frontmatter, or a\n * non-matching name ⇒ user-authored (left alone). Best-effort: any IO error\n * ⇒ treat as user-owned (the safe default — never silently delete). */\nasync function isUserOwnedSkillDir(dirAbs: string): Promise<boolean> {\n try {\n const skillMd = await readFile(join(dirAbs, 'SKILL.md'), 'utf8');\n const m = skillMd.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!m) return true; // no frontmatter at all → user-authored\n const fm = parseYaml(m[1] ?? '') as { name?: unknown };\n const dirName = basename(dirAbs);\n return typeof fm?.name !== 'string' || fm.name !== dirName;\n } catch {\n return true; // ENOENT (no SKILL.md) or parse error → user-authored (safe)\n }\n}\n\n/** `assertNotUserOwned` for the cursor flat layout: a `noir-*.mdc` file\n * is Noir-managed iff its YAML frontmatter carries `alwaysApply:` (the cursor\n * shape the compiler always emits). Missing frontmatter / no `alwaysApply` ⇒\n * user-authored. */\nasync function isUserOwnedMdc(fileAbs: string): Promise<boolean> {\n try {\n const md = await readFile(fileAbs, 'utf8');\n const m = md.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!m) return true;\n const fm = parseYaml(m[1] ?? '') as { alwaysApply?: unknown };\n return fm?.alwaysApply === undefined;\n } catch {\n return true;\n }\n}\n\n/** `assertNotUserOwned` for the cursor flat-layout's legacy-nested-dir\n * case: a Noir sync left `noir-{name}/noir-{name}.mdc` behind. The dir\n * is Noir-managed iff at least one `.mdc` inside carries canonical cursor\n * frontmatter (`alwaysApply:`). Absent / hand-rolled content ⇒ user-authored.\n * Empty dir ⇒ treat as user-owned (safe — never silently delete). */\nasync function isUserOwnedCursorLegacyDir(dirAbs: string): Promise<boolean> {\n try {\n const entries = await readdir(dirAbs);\n if (entries.length === 0) return true;\n let sawCanonical = false;\n for (const name of entries) {\n if (!name.endsWith('.mdc')) continue;\n if (!(await isUserOwnedMdc(join(dirAbs, name)))) {\n sawCanonical = true;\n break;\n }\n }\n return !sawCanonical;\n } catch {\n return true; // ENOENT or unreadable → safe default (preserve)\n }\n}\n\n/** sha256 hex, first 12 chars (matches @noir-ai/create's sha256Hex12). */\nfunction sha256Hex12(s: string): string {\n return createHash('sha256').update(s, 'utf8').digest('hex').slice(0, 12);\n}\n\n/** LCS similarity in [0,1]. Local copy (skills has no create dependency)\n * so the structured report's `similarity` matches the engine's algorithm. */\nfunction similarity(a: string, b: string): number {\n if (a === b) return 1;\n const A = a.split('\\n');\n const B = b.split('\\n');\n if (A.length === 0 && B.length === 0) return 1;\n const n = A.length;\n const m = B.length;\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array<number>(m + 1).fill(0));\n for (let i = n - 1; i >= 0; i--) {\n const dpi = dp[i] ?? [];\n const dpi1 = dp[i + 1] ?? [];\n const ai = A[i] ?? '';\n for (let j = m - 1; j >= 0; j--) {\n const bj = B[j] ?? '';\n dpi[j] = ai === bj ? (dpi1[j + 1] ?? 0) + 1 : Math.max(dpi1[j] ?? 0, dpi[j + 1] ?? 0);\n }\n }\n const lcs = dp[0]?.[0] ?? 0;\n return n + m === 0 ? 1 : (2 * lcs) / (n + m);\n}\n\n/** Pick a fresh `<abs><suffix>` aside path that does NOT exist (mirrors\n * @noir-ai/create's `uniqueAside`). Used by the `rename`/`duplicate` resolutions\n * so the user's bytes never get silently clobbered. */\nasync function renameAside(abs: string, suffix: string): Promise<void> {\n const { rename } = await import('node:fs/promises');\n const dest = await uniqueAsideAbs(abs, suffix);\n await rename(abs, dest);\n}\n\nasync function uniqueAside(abs: string, suffix: string): Promise<string> {\n return uniqueAsideAbs(abs, suffix);\n}\n\nasync function uniqueAsideAbs(abs: string, suffix: string): Promise<string> {\n const { existsSync } = await import('node:fs');\n let candidate = `${abs}${suffix}`;\n for (let n = 1; existsSync(candidate); n++) candidate = `${abs}${suffix}.${n}`;\n return candidate;\n}\n\n// Convenience for callers/tests that already hold raw markdown (unused by emit path;\n// kept so adapters/tests can validate a single in-memory skill without a dir).\nexport { discoverAll, discoverBuiltin };\n","import { readdirSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseFrontmatter } from './compiler.js';\nimport { parseIntegration } from './integrations-schema.js';\nimport type { BuiltinSkill, IntegrationDeclaration, IntegrationSkill } from './types.js';\n\n// Package root = ONE level up from THIS module's directory. Works in every layout\n// because `builtin/` is always a sibling of the dir this code runs from:\n// - vitest: .../packages/skills/src/discover.ts -> HERE = src -> PKG_ROOT = package root\n// - built: .../packages/skills/dist/index.js -> HERE = dist -> PKG_ROOT = package root\n// (tsup bundles everything into dist/index.js; `dirname` still yields `dist`.)\nconst HERE = dirname(fileURLToPath(import.meta.url)); // .../src OR .../dist\nconst PKG_ROOT = dirname(HERE); // .../packages/skills\nexport const BUILTIN_DIR = join(PKG_ROOT, 'builtin');\nexport const INTEGRATIONS_DIR = join(PKG_ROOT, 'integrations');\n\n/** Read a `<name>/SKILL.md` + its optional `references/` directory. Shared by\n * builtin + integration discovery (both share the SKILL.md + references shape;\n * the difference is the `integration.json` declaration). */\nfunction readSkillMd(dir: string): Pick<BuiltinSkill, 'skillMd' | 'frontmatter' | 'references'> {\n const skillMd = readFileSync(join(dir, 'SKILL.md'), 'utf8');\n const frontmatter = parseFrontmatter(skillMd);\n const refsDir = join(dir, 'references');\n let references: BuiltinSkill['references'] = [];\n try {\n references = readdirSync(refsDir)\n .filter((f) => f.endsWith('.md'))\n .sort()\n .map((f) => ({ name: f, content: readFileSync(join(refsDir, f), 'utf8') }));\n } catch {\n references = []; // no references/ dir is fine\n }\n return { skillMd, frontmatter, references };\n}\n\nexport function discoverBuiltin(builtinDir: string = BUILTIN_DIR): BuiltinSkill[] {\n const dirs = readdirSync(builtinDir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && e.name.startsWith('noir-'))\n .map((e) => e.name)\n .sort();\n return dirs.map((name) => {\n const dir = join(builtinDir, name);\n return { name, dir, ...readSkillMd(dir) };\n });\n}\n\n/**\n * Discover every shipped integration under `integrations/<name>/` (sibling of\n * `builtin/`). Each entry is a builtin-shaped skill (validated `SKILL.md` +\n * optional references) PLUS a parsed + validated `integration.json`\n * declaration. The dir is resolved relative to the package root by default —\n * when `integrationsDir` is overridden (tests), only that dir is scanned.\n *\n * Throws an aggregate error if any integration is malformed (missing\n * `SKILL.md`, bad frontmatter, or an `integration.json` that fails the Zod\n * schema) — fail-fast, atomic-ish, mirroring the builtin compiler's posture.\n */\nexport function discoverIntegrations(\n integrationsDir: string = INTEGRATIONS_DIR,\n): IntegrationSkill[] {\n let entries: string[];\n try {\n entries = readdirSync(integrationsDir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && e.name.startsWith('noir-'))\n .map((e) => e.name)\n .sort();\n } catch {\n // No integrations/ dir (or unreadable) = no integrations shipped here.\n return [];\n }\n return entries.map((name) => {\n const dir = join(integrationsDir, name);\n const declarationRaw = JSON.parse(\n readFileSync(join(dir, 'integration.json'), 'utf8'),\n ) as unknown;\n let declaration: IntegrationDeclaration;\n try {\n declaration = parseIntegration(declarationRaw);\n } catch (err) {\n // ZodError → wrap with the integration's dir for an actionable message.\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Invalid integration.json in ${dir}: ${msg}`);\n }\n if (declaration.name !== name) {\n throw new Error(\n `Integration dir \"${name}\" must equal declaration name \"${declaration.name}\"`,\n );\n }\n return { name, dir, ...readSkillMd(dir), declaration, declarationRaw };\n });\n}\n\n/**\n * Discover the full shipped pack — builtins + integrations — in one call. Used\n * by `emitSkillsToDir` so `noir init`/`sync` emit BOTH (slice X goal). The\n * `integrationsDir` defaults to the sibling of `builtinDir` so a test fixture\n * that overrides only `builtinDir` cleanly resolves integrations to a\n * non-existent path (returns `[]`) instead of accidentally picking up the\n * shipped integrations.\n */\nexport function discoverAll(opts: { builtinDir?: string; integrationsDir?: string } = {}): {\n builtins: BuiltinSkill[];\n integrations: IntegrationSkill[];\n} {\n const builtinDir = opts.builtinDir ?? BUILTIN_DIR;\n const integrationsDir = opts.integrationsDir ?? join(dirname(builtinDir), 'integrations');\n return {\n builtins: discoverBuiltin(builtinDir),\n integrations: discoverIntegrations(integrationsDir),\n };\n}\n","// `integration.json` schema. Each integration ships a declaration\n// alongside its `SKILL.md` so the compiler can validate the auth/runtime/SDD\n// contract uniformly and so the host adapter knows whether to widen emission\n// (host MCP config) or stay skill-only.\n//\n// Doctrine (slice-x spec §architecture + v1x §4.4): skill-only by default;\n// `gated-write-proxy` for stateless writes routed through a Noir MCP tool\n// `mcp-stdio` for a Noir-spawned stateful runtime (deferred, gated on\n// keychain); `external-mcp` for a config-only pointer at a first-party/community\n// MCP server (emits a host `.mcp.json` server entry, no Noir runtime).\n//\n// Zod v4 (`z.record(keyType, valueType)` is key-first). The schema is additive\n// and defaults to the safest tier — every optional field degrades to \"skill\n// only, manual paste, no host MCP wiring\" — so a minimal `{ \"name\":\n// \"noir-x\" }` declaration still parses + behaves as a pure playbook.\n\nimport * as z from 'zod';\n\n/** Auth shape. Locked: `env-var` only until keychain lands (Q4b — refuse OAuth,\n * never silently lower the security bar). `fallback:'manual-paste'` keeps the\n * no-token path honest (the playbook tells the user to paste a value); `'none'`\n * is for integrations that genuinely do not need a token (read-only public\n * endpoints). */\nexport const IntegrationAuthSchema = z.object({\n type: z.literal('env-var'),\n tokenEnv: z.string().min(1),\n fallback: z.enum(['manual-paste', 'none']).default('manual-paste'),\n});\n\n/** SDD two-way binding. `intakeFrom` declares the external artifact kind the\n * `noir-brainstorming` skill pulls from (absorbed noir-intake in C3);\n * `writeBack` enumerates the fields `noir-wrap` pushes back at session end\n * (absorbed noir-document in C3). Strings (not enums) for `writeBack` so a\n * per-integration vocabulary stays expressible without\n * churning the schema. */\nexport const IntegrationSddSchema = z\n .object({\n intakeFrom: z.enum(['task', 'issue', 'none']).optional(),\n writeBack: z.array(z.string()).default([]),\n })\n .default({ writeBack: [] });\n\n/** Host MCP declaration. Only meaningful when `runtime ∈ {mcp-stdio,\n * external-mcp}`; `null` for `none`/`gated-write-proxy` (ClickUp — writes go\n * through Noir's own MCP tool, never the host's). */\nexport const IntegrationMcpSchema = z\n .object({\n command: z.string().min(1),\n transport: z.enum(['stdio', 'http']),\n args: z.array(z.string()).optional(),\n url: z.string().optional(),\n env: z.record(z.string(), z.string()).optional(),\n })\n .nullable()\n .default(null);\n\n/** Full `integration.json` declaration. `name` enforces the `noir-` prefix\n * shared with builtins so the unified pack reads consistently. */\nexport const IntegrationDeclarationSchema = z.object({\n name: z.string().regex(/^noir-[a-z0-9]+(?:-[a-z0-9]+)*$/, 'must match noir-<kebab>'),\n auth: IntegrationAuthSchema,\n runtime: z.enum(['none', 'gated-write-proxy', 'mcp-stdio', 'external-mcp']),\n sdd: IntegrationSddSchema,\n mcp: IntegrationMcpSchema,\n});\n\nexport type IntegrationDeclaration = z.infer<typeof IntegrationDeclarationSchema>;\n\n/** Coerce + validate an `integration.json` payload. Throws a ZodError on a\n * malformed declaration — the caller (discover) wraps it with the\n * integration's dir for an actionable message. */\nexport function parseIntegration(json: unknown): IntegrationDeclaration {\n return IntegrationDeclarationSchema.parse(json);\n}\n\n/** Non-throwing variant for the hygiene tests + lint helpers. */\nexport function validateIntegration(\n obj: unknown,\n): { ok: true; value: IntegrationDeclaration } | { ok: false; errors: string[] } {\n const parsed = IntegrationDeclarationSchema.safeParse(obj);\n if (parsed.success) return { ok: true, value: parsed.data };\n return { ok: false, errors: parsed.error.issues.map((i: { message: string }) => i.message) };\n}\n\n/** Runtime → host-MCP emission predicate. The compiler widens emission (makes\n * the host MCP block available to the adapter) ONLY for these tiers. The\n * adapter ultimately decides per-host what to render — for Claude, `external-\n * mcp` becomes a `.mcp.json` server entry; `mcp-stdio` registers through the\n * existing `noir mcp serve --stdio` entry so no NEW server entry is emitted. */\nexport function runtimeEmitsHostMcp(runtime: IntegrationDeclaration['runtime']): boolean {\n return runtime === 'mcp-stdio' || runtime === 'external-mcp';\n}\n","// Structural quality checks for the builtin skill pack — the C3 quality gate.\n//\n// Split from compiler.ts so `validateSkill` (hard errors) and `lintSkill` (soft\n// warnings) share one source of truth for the body-structure rules, and so the\n// rules are unit-testable in isolation. Canonical template (C3 spec §3):\n//\n// Overview → When to use → Procedure → Verification → Notes\n//\n// Rules here implement the researched canon (Anthropic best practices, the\n// agentskills.io spec, Claude Code docs): required sections, a <500-line body\n// budget, one-level-deep references (no chained refs), and a WHAT+WHEN\n// description. `checkRequiredSections` is the load-bearing check — it's what\n// forces every SKILL.md to carry a real playbook shape, not a shell.\n\nimport { ARTIFACT_TYPES } from '@noir-ai/core';\nimport type { BuiltinSkill } from './types.js';\n\n/** The max body length the canon recommends (Anthropic: \"under 500 lines\").\n * SKILL.md is a navigator, not a repository — split to references/ past this. */\nexport const MAX_BODY_LINES = 500;\n\n/** The floor for a full (non-stub) playbook body. Stubs were ~13 lines; a real\n * playbook needs at least this much substance to be loadable as guidance. */\nexport const MIN_FULL_BODY_LINES = 20;\n\n/** The canonical heading a skill MUST carry to describe its trigger conditions.\n * Accepts the C3 template spelling AND common variants so existing skills\n * aren't forced into a single casing. */\nconst WHEN_SECTION = /^## When to use$/im;\n\n/** The canonical heading a skill MUST carry for its numbered workflow. `## Steps`\n * is accepted for backward-compat with noir-wrap; `## Procedure` is canonical. */\nconst PROCEDURE_SECTION = /^## (Procedure|Steps)$/im;\n\n/** One of these closing sections must exist — evidence/notes/degradation. */\nconst CLOSING_SECTION = /^## (Verification|Notes|Fallbacks|Troubleshooting|Why order matters)$/im;\n\n/** The lead cue regex — the FIRST part of the description must be a trigger. */\nconst WHEN_CUE =\n /^(use|using|used|whenever|when|before|after|while|starting|encountering|completing|creating|about to|upon|during|to|for|on)\\b/i;\n\n/** A chained reference link — a reference file that links to ANOTHER reference\n * file (`references/a.md` → `references/b.md` or `../references/b.md`). The\n * canon forbids deep nesting (agentskills.io: \"no chained references\"). */\nconst CHAINED_REF_RE = /\\]\\((\\/?\\.?\\.?\\/)?(references\\/|\\.\\.\\/references\\/)/i;\n\n/**\n * Which required sections are missing from a skill body. Returns a list of\n * human-readable section names, e.g. `['## When to use', '## Verification']`.\n * Empty array = the body carries the canonical playbook shape.\n */\nexport function missingSections(body: string): string[] {\n const missing: string[] = [];\n if (!WHEN_SECTION.test(body)) missing.push('## When to use');\n if (!PROCEDURE_SECTION.test(body)) missing.push('## Procedure (or ## Steps)');\n if (!CLOSING_SECTION.test(body)) {\n missing.push('one of ## Verification / ## Notes / ## Fallbacks / ## Troubleshooting');\n }\n return missing;\n}\n\n/** True when the body is within the canon line budget. */\nexport function withinLineBudget(body: string, max: number = MAX_BODY_LINES): boolean {\n return body.split('\\n').length <= max;\n}\n\n/** Reference files that point at another reference file (chained — forbidden).\n * Returns the offending reference names. */\nexport function chainedReferences(skill: BuiltinSkill): string[] {\n return skill.references.filter((r) => CHAINED_REF_RE.test(r.content)).map((r) => r.name);\n}\n\n/**\n * True when the description carries BOTH a WHEN trigger lead AND a WHAT clause.\n * The C3 rule: `description` MUST lead with a WHEN cue (existing compiler rule)\n * AND contain a compact WHAT clause naming what the skill does.\n *\n * Two canonical shapes both pass:\n * \"Use when turning an idea into a spec — draft the spec.\" (WHEN → WHAT)\n * \"Use when a task writes back to ClickUp: update its status.\" (WHEN → WHAT)\n * The description MUST lead with a cue (the WHEN-only and WHAT-only schools are\n * both rejected). The WHAT clause is the part after the trigger phrase — split\n * on an em/en dash or a period that ends the trigger phrase. A real WHAT clause\n * has ≥3 words naming the action, so a bare \"Use when…\" fails.\n */\nexport function isWhatWhenDescription(description: string): boolean {\n const trimmed = description.trim();\n if (!trimmed) return false;\n if (!WHEN_CUE.test(trimmed)) return false; // must lead with a cue\n // Split off the trigger phrase: after an em/en dash, or after the first period.\n // \"Use when X — draft the spec.\" → whatPart = \"draft the spec.\"\n const whatPart = trimmed\n .split(/[—–]|(?<=\\.) /)\n .slice(1)\n .join(' ')\n .trim();\n return whatPart.split(/\\s+/).filter(Boolean).length >= 3;\n}\n\n/** True when the description leads with a WHEN cue (the existing rule, kept\n * here so validate + lint + hygiene share one implementation). */\nexport function looksLikeWhenDescription(description: string): boolean {\n return WHEN_CUE.test(description.trim());\n}\n\n/**\n * Soft-quality warnings for `lintSkill`. Each returns a short rule id + message.\n * These are advisory — a skill can pass `validateSkill` and still carry lint\n * warnings that the author should resolve. Rules are drawn from the researched\n * anti-pattern list (no examples, thin body, first/second person narration,\n * voodoo constants, time-sensitive version pins).\n */\nexport function lintWarnings(skill: BuiltinSkill): string[] {\n const warnings: string[] = [];\n const body = skill.skillMd; // linting the whole file (frontmatter + body) is fine for prose checks\n const bodyLines = body.split('\\n').length;\n\n if (bodyLines < MIN_FULL_BODY_LINES + 6) {\n // frontmatter is ~6 lines; a full playbook should clear 20 body lines\n warnings.push('thin-body: full playbook body is under 20 lines');\n }\n // Concrete examples: at least one fenced block or an \"example:\" mention.\n const hasExample = /```/.test(body) || /\\bexample:?\\b/i.test(body) || /\\be\\.g\\.\\b/i.test(body);\n if (!hasExample) {\n warnings.push('no-example: no concrete code fence or worked example in the body');\n }\n // First/second-person narration — \"I/we/you\" as the agent doing work.\n if (\n /\\b(I|we|you)\\s+(will|should|can|need|must|do|write|create|implement|run|build)\\b/i.test(body)\n ) {\n warnings.push('first-person: narration addresses the reader instead of imperative steps');\n }\n // Voodoo constants: a bare number with a magnitude claim but no justification\n // (\"wait 5 seconds\" without why). Very loose — catches \"wait N seconds\" without\n // a \"why\".\n if (\n /\\b(?:wait|sleep|retry|backoff|limit|cap)\\s+[a-z]*\\s*(\\d{1,4})\\b/i.test(body) &&\n !/because|to (avoid|prevent|give|let)/i.test(body)\n ) {\n warnings.push('voodoo-constant: numeric threshold without a stated reason');\n }\n // Time-sensitive version pins outside a Legacy section.\n const hasVersionPin = /as of \\d{4}|\\bversion \\d+\\.\\d+\\.\\d+\\b|\"v\\d+\\.\\d+\"/i.test(body);\n const hasLegacySection = /^## Legacy|^## Old patterns/i.test(body);\n if (hasVersionPin && !hasLegacySection) {\n warnings.push('time-sensitive: version/date pin outside a Legacy section');\n }\n return warnings;\n}\n\n/** `.noir/` subdirectories a skill may legitimately reference that are NOT\n * per-type artifact dirs (store db, audit export, rules, daemon state). */\nconst NON_ARTIFACT_DIRS = new Set(['store', 'audit', 'rules', 'state']);\n\n/** Canonical artifact directory → its type code(s) (from the C3 registry).\n * One directory can host multiple kinds (`subagents/` → BR + RP). */\nconst DIR_TO_CODES: ReadonlyMap<string, ReadonlySet<string>> = (() => {\n const m = new Map<string, Set<string>>();\n for (const t of Object.values(ARTIFACT_TYPES)) {\n const codes = m.get(t.dir);\n if (codes) codes.add(t.code);\n else m.set(t.dir, new Set([t.code]));\n }\n return m;\n})();\n\n/**\n * `.noir/…` output-path drift: a skill body or reference that names a `.noir/`\n * directory absent from the artifact registry (e.g. `.noir/sdd/`), or a file\n * under a canonical artifact directory whose name carries none of that\n * directory's type codes (e.g. `.noir/plans/<date>-<slug>.md` instead of\n * `PL-<NNNN>-…`). Hard errors — the host would mint non-standard artifacts.\n * See `docs/reference/artifact-format.md`.\n */\nexport function artifactPathDrift(skill: BuiltinSkill): string[] {\n const texts = [skill.skillMd, ...skill.references.map((r) => r.content)];\n const drifts = new Set<string>();\n // `.noir/<dir>/<name>` — name runs to whitespace/backtick/quote/paren so\n // `<date>-<slug>.md` placeholders are captured whole.\n const re = /\\.noir\\/([a-zA-Z0-9-]+)\\/([^\\s`'\"()]+)/g;\n for (const text of texts) {\n for (const m of text.matchAll(re)) {\n const dir = m[1];\n const name = m[2];\n if (!dir || !name) continue;\n const codes = DIR_TO_CODES.get(dir);\n if (codes === undefined) {\n if (!NON_ARTIFACT_DIRS.has(dir)) {\n drifts.add(`.noir/${dir}/ is not a canonical artifact directory (C3 artifact standard)`);\n }\n continue;\n }\n const expected = [...codes].join('/');\n if (![...codes].some((c) => name.startsWith(`${c}-`))) {\n drifts.add(`.noir/${dir}/${name} must be ${expected}-<NNNN>-… (C3 artifact standard)`);\n }\n }\n }\n return [...drifts];\n}\n","// Offline behavioral-eval harness for the skill pack — C3.\n//\n// Skills are declared as `evals/**/evals.json` per the agentskills.io format\n// (`{skill_name, evals:[{id, prompt, expected_output, assertions?}]}`). The\n// harness loads every suite and runs OFFLINE assertions only — `contains`,\n// `not-contains`, `regex`, `length-gte` — no LLM, no network, so it runs in\n// `pnpm test` (the project's offline/free constraint). This is the executable\n// that turns the JSON declaration into a vitest suite (see test/evals.test.ts).\n//\n// The evals are deliberately simple: they assert that a skill's PROMPT → a\n// simulated agent answer satisfies structural expectations. They are NOT\n// LLM-judge evals (deferred to a future slice, ADR'd); they are the CI-safe\n// baseline that catches regressions in the skill's core directives.\n\nimport { readdirSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/** A single offline assertion on the produced output. */\nexport type EvalAssertion =\n | { type: 'contains'; value: string }\n | { type: 'not-contains'; value: string }\n | { type: 'regex'; value: string }\n | { type: 'length-gte'; value: number };\n\n/** One eval case: a prompt + the expected directive the skill must produce. */\nexport interface SkillEval {\n id: string;\n prompt: string;\n expected_output: string;\n assertions?: EvalAssertion[];\n}\n\n/** A suite = one skill's evals (the `evals.json` shape). */\nexport interface EvalSuite {\n skill_name: string;\n evals: SkillEval[];\n}\n\n/** Validate an assertion's shape (fail-fast on a malformed evals.json). */\nfunction parseAssertion(a: unknown): EvalAssertion {\n if (typeof a !== 'object' || a === null) throw new Error('assertion must be an object');\n const { type, value } = a as { type?: unknown; value?: unknown };\n switch (type) {\n case 'contains':\n case 'not-contains':\n case 'regex':\n if (typeof value !== 'string' || value.length === 0) {\n throw new Error(`assertion '${String(type)}' requires a non-empty string value`);\n }\n return { type, value } as EvalAssertion;\n case 'length-gte':\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new Error(\"assertion 'length-gte' requires a finite number value\");\n }\n return { type, value } as EvalAssertion;\n default:\n throw new Error(`unknown assertion type '${String(type)}'`);\n }\n}\n\n/** Parse + validate a raw `evals.json` payload. Throws on malformed shape. */\nexport function parseEvalSuite(raw: unknown): EvalSuite {\n if (typeof raw !== 'object' || raw === null) throw new Error('evals.json must be an object');\n const { skill_name, evals } = raw as { skill_name?: unknown; evals?: unknown };\n if (typeof skill_name !== 'string' || skill_name.length === 0) {\n throw new Error('evals.json requires a non-empty string `skill_name`');\n }\n if (!Array.isArray(evals) || evals.length === 0) {\n throw new Error(`evals.json for ${skill_name} requires a non-empty 'evals' array`);\n }\n const parsed: SkillEval[] = evals.map((e, i) => {\n if (typeof e !== 'object' || e === null) throw new Error(`eval[${i}] must be an object`);\n const { id, prompt, expected_output, assertions } = e as {\n id?: unknown;\n prompt?: unknown;\n expected_output?: unknown;\n assertions?: unknown;\n };\n if (typeof id !== 'string' || id.length === 0)\n throw new Error(`eval[${i}] requires a string id`);\n if (typeof prompt !== 'string' || prompt.length === 0) {\n throw new Error(`eval ${id} requires a string prompt`);\n }\n if (typeof expected_output !== 'string') {\n throw new Error(`eval ${id} requires a string expected_output`);\n }\n return {\n id,\n prompt,\n expected_output,\n assertions: Array.isArray(assertions) ? assertions.map(parseAssertion) : undefined,\n };\n });\n return { skill_name, evals: parsed };\n}\n\n/** Run a list of assertions against `output`. Returns pass + per-assertion failures. */\nexport function runAssertions(\n output: string,\n assertions: EvalAssertion[],\n): { pass: boolean; failures: string[] } {\n const failures: string[] = [];\n for (const a of assertions) {\n switch (a.type) {\n case 'contains':\n if (!output.includes(a.value)) failures.push(`expected to contain \"${a.value}\"`);\n break;\n case 'not-contains':\n if (output.includes(a.value)) failures.push(`expected NOT to contain \"${a.value}\"`);\n break;\n case 'regex':\n if (!new RegExp(a.value, 'i').test(output)) failures.push(`expected to match /${a.value}/`);\n break;\n case 'length-gte':\n if (output.length < a.value)\n failures.push(`expected length >= ${a.value} (got ${output.length})`);\n break;\n }\n }\n return { pass: failures.length === 0, failures };\n}\n\n// Package root (evals/ is a sibling of src/): resolve the evals dir the same\n// way discover.ts resolves builtin/. Works under vitest (src) + built (dist).\nconst HERE = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = dirname(HERE);\nexport const EVALS_DIR = join(PKG_ROOT, 'evals');\n\n/**\n * Load every eval suite under `dir` (default the shipped `evals/` dir — each\n * subdir carries an `evals.json`). Skips non-`.json` files and missing dirs\n * (no evals = no-op). Fail-fast on a malformed suite (a broken evals.json is a\n * bug, not a skip).\n */\nexport function loadEvalSuites(dir: string = EVALS_DIR): EvalSuite[] {\n let entries: string[];\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort();\n } catch {\n return []; // no evals/ dir (or unreadable) = no suites shipped\n }\n const suites: EvalSuite[] = [];\n for (const name of entries) {\n const file = join(dir, name, 'evals.json');\n try {\n const raw = JSON.parse(readFileSync(file, 'utf8')) as unknown;\n suites.push(parseEvalSuite(raw));\n } catch (err) {\n // Missing evals.json in a subdir is fine (it may be a scratch dir); a\n // malformed one is not. Distinguish by existence.\n try {\n readFileSync(file, 'utf8');\n throw err; // exists but malformed → rethrow\n } catch (re) {\n if (re === err) throw err;\n // ENOENT → skip silently.\n }\n }\n }\n return suites;\n}\n\n/** Evaluate one suite: for each eval, check its assertions against\n * `expected_output` (the directive the skill should produce). Returns pass/fail\n * per eval with the assertion failures. This is the offline core the vitest\n * runner drives. */\nexport function evaluateSuite(\n suite: EvalSuite,\n): Array<{ id: string; pass: boolean; failures: string[] }> {\n return suite.evals.map((e) => ({\n id: e.id,\n pass: e.assertions ? runAssertions(e.expected_output, e.assertions).pass : true,\n failures: e.assertions ? runAssertions(e.expected_output, e.assertions).failures : [],\n }));\n}\n","// Runtime-derived skill registry — C3.\n//\n// The registry is NOT a committed file: frontmatter is the single source of\n// truth and `buildRegistry()` derives a queryable index from `discoverAll()`\n// on demand (the CLI calls it; no drift risk, per the C3 decision D3). Every\n// field comes from either frontmatter (`metadata.category`, `metadata.version`)\n// or a shape check (`status` from the stub marker, `referenceCount`/`lines`\n// from the discovered files).\n\nimport { discoverAll } from './discover.js';\nimport type { BuiltinSkill } from './types.js';\n\n/** The pack-wide `noir-` namespace a registry entry always belongs to. */\nexport const NOIR_NAMESPACE = 'noir-';\n\n/** One row in the derived registry. */\nexport interface SkillRegistryEntry {\n /** Canonical `noir-<kebab>` id (== dir name). */\n name: string;\n /** `builtin` for the shipped pack; `integration` for an `integrations/<name>/`. */\n kind: 'builtin' | 'integration';\n /** Category from `metadata.category`; falls back to the `noir-`-stripped name\n * (a newly-authored skill still gets a sensible cell). */\n category: string;\n /** Per-skill version from `metadata.version`; defaults to `0.0.0` when absent\n * (the pack-level version in package.json is the authoritative release). */\n version: string;\n /** `full` when the body has no `> **Stub:**` marker; `stub` otherwise. */\n status: 'full' | 'stub';\n /** Lifecycle stage of the skill. `active` = currently shipped and usable;\n * `deprecated` = superseded (a renamed/merged predecessor). C3 spec §8.\n * The curated pack has no deprecated members today. */\n lifecycle: 'active' | 'deprecated';\n /** The WHAT+WHEN description (the trigger the host sees). */\n description: string;\n /** Number of `references/*.md` files. */\n referenceCount: number;\n /** Total SKILL.md line count (frontmatter + body). */\n lines: number;\n}\n\n/** Derive the category from the skill name when `metadata.category` is absent. */\nfunction fallbackCategory(name: string): string {\n return name.replace(/^noir-/, '') || 'general';\n}\n\n/** Map a discovered builtin/integration to a registry row. */\nfunction toEntry(s: BuiltinSkill, kind: 'builtin' | 'integration'): SkillRegistryEntry {\n const category = s.frontmatter.metadata?.category?.trim() || fallbackCategory(s.name);\n const version = s.frontmatter.metadata?.version?.trim() || '0.0.0';\n const status: 'full' | 'stub' = s.skillMd.includes('> **Stub:**') ? 'stub' : 'full';\n const lines = s.skillMd.split('\\n').length;\n return {\n name: s.name,\n kind,\n category,\n version,\n status,\n // Every shipped skill is active; `deprecated` is reserved for a future\n // superseded member (C3 spec §8).\n lifecycle: 'active' as const,\n description: typeof s.frontmatter.description === 'string' ? s.frontmatter.description : '',\n referenceCount: s.references.length,\n lines,\n };\n}\n\n/**\n * Build the full registry from `discoverAll()` — builtins + integrations in\n * one list, sorted by name. Pure (no I/O beyond discovery); never throws for\n * a malformed skill (a discovery failure surfaces upstream, the same as the\n * compiler's fail-fast).\n */\nexport function buildRegistry(): SkillRegistryEntry[] {\n const { builtins, integrations } = discoverAll();\n return [\n ...builtins.map((b) => toEntry(b, 'builtin')),\n ...integrations.map((i) => toEntry(i, 'integration')),\n ].sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/** Convenience: registry filtered to a single category (CLI grouping). */\nexport function registryByCategory(category: string): SkillRegistryEntry[] {\n return buildRegistry().filter((e) => e.category === category);\n}\n","// Tokens that must NOT survive a port (predecessor plugin internals + Superpowers\n// rhetoric). Shared by the compiler's lint helper and the hygiene tests (T5).\nexport const FORBIDDEN_RESIDUE: readonly string[] = [\n 'workflow/<task', // predecessor state file\n 'noir-workflow.mode', // predecessor mode flag\n 'noir-workflow', // predecessor plugin name (as a plugin/path reference)\n '@uiigateway', // predecessor Angular specifics\n // NOTE: 'ClickUp'/'clickup' were forbidden during the predecessor-port era (the\n // ClickUp REST precedent lived in the deleted noir-workflow plugin). ClickUp is\n // now reintroduced as a first-class Noir integration (skills/integrations/noir-clickup),\n // so the token is no longer residue — it is the integration's legitimate subject.\n '<EXTREMELY-IMPORTANT', // Superpowers rhetoric\n 'SUBAGENT-STOP', // Superpowers rhetoric\n 'plugins/noir-workflow', // predecessor path\n];\n"],"mappings":"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","names":["dirname","join","join","dirname","readdirSync","readFileSync","dirname","join","fileURLToPath","HERE","PKG_ROOT"]}
1
+ {"version":3,"sources":["../src/compiler.ts","../src/discover.ts","../src/integrations-schema.ts","../src/quality.ts","../src/evals.ts","../src/registry.ts","../src/residue.ts"],"sourcesContent":["import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { basename, dirname, join } from 'node:path';\nimport { sha256Hex12, uniqueAsideSync } from '@noir-ai/core';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { discoverAll, discoverBuiltin } from './discover.js';\nimport { runtimeEmitsHostMcp } from './integrations-schema.js';\nimport {\n artifactPathDrift,\n chainedReferences,\n isWhatWhenDescription,\n lintWarnings,\n looksLikeWhenDescription,\n MAX_BODY_LINES,\n missingSections,\n withinLineBudget,\n} from './quality.js';\n\n// Re-exported for backward-compat — callers (hygiene tests, CLI) import\n// `looksLikeWhenDescription` from './compiler.js'. The single source of truth\n// is quality.ts; this alias keeps the old import surface stable.\nexport { looksLikeWhenDescription } from './quality.js';\n\nimport type {\n BuiltinSkill,\n CompiledIntegration,\n CompiledSkill,\n CompileTarget,\n EmitSummary,\n IntegrationSkill,\n SkillConflict,\n SkillConflictContext,\n SkillConflictResolution,\n SkillConflictResolver,\n SkillFrontmatter,\n ValidationResult,\n} from './types.js';\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/;\nconst NAME_RE = /^noir-[a-z0-9]+(?:-[a-z0-9]+)*$/;\nconst MAX_DESC = 1024;\n\nexport function parseFrontmatter(md: string): SkillFrontmatter {\n const m = md.match(FRONTMATTER_RE);\n if (!m) throw new Error('Skill missing YAML frontmatter (expected --- ... ---)');\n // Group 1 always exists when the regex matches; guard past noUncheckedIndexedAccess.\n const yaml = m[1];\n if (yaml === undefined) throw new Error('Skill missing YAML frontmatter (expected --- ... ---)');\n const fm = parseYaml(yaml) as SkillFrontmatter;\n if (typeof fm?.name !== 'string' || typeof fm?.description !== 'string') {\n throw new Error('Skill frontmatter requires string `name` + `description`');\n }\n return fm;\n}\n\nexport function bodyOf(md: string): string {\n return md.replace(/^---\\r?\\n[\\s\\S]*?\\r?\\n---\\r?\\n?/, '');\n}\n\nexport function validateSkill(skill: BuiltinSkill): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const { name, description, metadata } = skill.frontmatter;\n if (!name) errors.push('missing `name`');\n else if (!NAME_RE.test(name)) errors.push(`name \"${name}\" must match noir-<kebab>`);\n if (basename(skill.dir) !== name) {\n errors.push(`dir \"${basename(skill.dir)}\" must equal name \"${name}\"`);\n }\n if (!description?.trim()) errors.push('missing `description`');\n else if (description.length > MAX_DESC) errors.push(`description exceeds ${MAX_DESC} chars`);\n else if (!looksLikeWhenDescription(description)) {\n errors.push('description must state WHEN to trigger (e.g. \"Use when…\"), not WHAT it does');\n } else if (!isWhatWhenDescription(description)) {\n // C3: the description must ALSO carry a WHAT clause — the trigger phrase\n // alone (WHEN-only) fails. Errors, not warnings: it's part of the contract.\n errors.push('description must be WHAT+WHEN — a WHAT clause after the trigger phrase');\n }\n // C3 structural gate: metadata, required sections, line budget, one-level refs.\n if (!metadata?.category?.trim()) errors.push('missing `metadata.category`');\n if (!metadata?.version?.trim()) errors.push('missing `metadata.version`');\n const body = bodyOf(skill.skillMd);\n const missing = missingSections(body);\n for (const sec of missing) errors.push(`missing required section: ${sec}`);\n if (!withinLineBudget(body)) {\n errors.push(`body exceeds ${MAX_BODY_LINES}-line budget (${body.split('\\n').length} lines)`);\n }\n const chained = chainedReferences(skill);\n for (const r of chained)\n errors.push(`reference \"${r}\" chains to another reference (must be one level deep)`);\n for (const r of skill.references) {\n if (!/^[a-z0-9-]+\\.md$/i.test(r.name)) errors.push(`reference \"${r.name}\" must be <kebab>.md`);\n if (!r.content.trim()) errors.push(`reference \"${r.name}\" is empty`);\n }\n // C3 generated-artifact standard: no `.noir/` output-path drift in the body\n // or references (unknown dir, or a filename missing its type-code prefix).\n for (const d of artifactPathDrift(skill)) errors.push(d);\n // Soft warnings (lint-level) — advisory, non-failing.\n warnings.push(...lintWarnings(skill));\n return { ok: errors.length === 0, errors, warnings: warnings.length > 0 ? warnings : undefined };\n}\n\n/**\n * `lintSkill` — the C3 soft quality gate. Errors = `validateSkill` errors (a\n * skill that fails validation is broken); warnings = `quality.ts` style rules\n * (thin body, no examples, first-person narration, …). A skill can validate\n * clean yet still carry lint warnings the author should resolve.\n */\nexport function lintSkill(skill: BuiltinSkill): {\n name: string;\n errors: string[];\n warnings: string[];\n} {\n const res = validateSkill(skill);\n return { name: skill.name, errors: res.errors, warnings: res.warnings ?? [] };\n}\n\n/**\n * Compile a builtin skill into the host-shaped `CompiledSkill`. The validator\n * runs for every target (a malformed skill is malformed in any format).\n *\n * - `claude` | `agents-md` | `gemini` | `opencode` (the AGENTS.md-aligned\n * hosts): canonical format copied verbatim — `SKILL.md` plus its\n * `references/` siblings. These hosts read the same SKILL.md shape;\n * the EMIT-LOCATION (which dir they land in) is the cli/adapter's job, not\n * the compiler's.\n * - `cursor`: transform into a Cursor `.mdc` rule — ONE file `<name>.mdc`\n * whose frontmatter carries the skill description, a wildcard globs entry,\n * and `alwaysApply: false` (the agent decides whether to pull the rule via\n * the description; never auto-applied). The body is the SKILL.md BODY\n * (frontmatter stripped). Cursor's rule format has no references concept,\n * so references are dropped (documented in the S10 spec risks — the body is\n * the surface).\n *\n * The `target` defaults to `'claude'` for backward compatibility with every\n * existing caller; the verbatim branch produces byte-identical output to v1.1\n * (the regression anchor).\n */\nexport function compileSkill(skill: BuiltinSkill, target: CompileTarget = 'claude'): CompiledSkill {\n const res = validateSkill(skill);\n if (!res.ok) throw new Error(`Cannot compile ${skill.name}: ${res.errors.join('; ')}`);\n if (target === 'cursor') {\n return { name: skill.name, files: [{ path: [`${skill.name}.mdc`], content: toMdc(skill) }] };\n }\n // claude / agents-md / gemini / opencode → canonical verbatim format.\n const files = [\n { path: ['SKILL.md'], content: skill.skillMd },\n ...skill.references.map((r) => ({ path: ['references', r.name], content: r.content })),\n ];\n return { name: skill.name, files };\n}\n\n/**\n * Cursor `.mdc` rule transform — frontmatter `{description, globs, alwaysApply:\n * false}` + the SKILL.md body. The description drives Cursor's agent-decided\n * rule selection (per the S10 spec open-decision default: `alwaysApply: false`).\n * Cursor's rule format has no references/ concept, so the body alone is the\n * surface; references are dropped (documented risk in the spec).\n *\n * Frontmatter is YAML-serialized via the `yaml` package (already a dep) so\n * description escaping (colons, quotes, multi-line) is correct without hand\n * rolling. `lineWidth: 0` keeps descriptions on a single quoted line (predictable\n * byte shape; Cursor tolerates either). */\nfunction toMdc(skill: BuiltinSkill): string {\n const frontmatter = stringifyYaml(\n {\n description: skill.frontmatter.description,\n globs: ['**/*'],\n alwaysApply: false,\n },\n { lineWidth: 0 },\n ).trimEnd();\n const body = bodyOf(skill.skillMd);\n // Cursor's `.mdc` shape: leading `---\\n`, frontmatter, closing `---\\n`, blank\n // line, body. Trailing newline so files concatenate cleanly on disk.\n return `---\\n${frontmatter}\\n---\\n${body.endsWith('\\n') ? body : `${body}\\n`}`;\n}\n\n/**\n * Compile an integration: validates the SKILL.md (same shape as builtins) +\n * renders the same per-skill files. When the integration's `runtime` widens\n * emission (`mcp-stdio`/`external-mcp`) AND it carries a non-null `mcp`\n * declaration, also expose a provider-neutral `hostMcp` server block the host\n * adapter may merge into the host MCP config (e.g. Claude's `.mcp.json`).\n *\n * For `gated-write-proxy` (ClickUp) + `none` → no `hostMcp` (writes route\n * through Noir's own MCP tool; the host MCP config is unchanged). For\n * `mcp-stdio` the hostMcp is exposed (the compiler widens emission); the\n * Claude adapter still does not emit a NEW server entry for it (Noir's own\n * `noir mcp serve` entry already covers it).\n */\nexport function compileIntegration(\n integration: IntegrationSkill,\n target: CompileTarget = 'claude',\n): CompiledIntegration {\n // Validate the skill half first (throws on a bad SKILL.md — same path as\n // builtins). compileSkill returns a base CompiledSkill; we layer hostMcp on.\n const base = compileSkill(integration, target);\n const { runtime, mcp } = integration.declaration;\n if (runtimeEmitsHostMcp(runtime) && mcp !== null) {\n return {\n ...base,\n hostMcp: {\n serverName: integration.name,\n command: mcp.command,\n args: mcp.args,\n transport: mcp.transport,\n url: mcp.url,\n env: mcp.env,\n },\n };\n }\n return base;\n}\n\nexport async function emitSkillsToDir(\n targetDir: string,\n opts: {\n builtinDir?: string;\n integrationsDir?: string;\n includeIntegrations?: boolean;\n /** S10 CompileTarget — selects the per-skill transform. Defaults to\n * `'claude'` (the v1.1 verbatim SKILL.md shape) so every existing caller\n * stays byte-identical. `'cursor'` compiles each skill to a `.mdc` rule\n * file (frontmatter `description`/`globs`/`alwaysApply:false` + the\n * SKILL.md body); the other targets map to the verbatim shape (their\n * hosts have no skill concept, so emit is skipped upstream — but the\n * default-verbatim policy keeps the signature total). */\n target?: CompileTarget;\n /** Policy for a skill file that exists AND differs from the compiled\n * bytes, when no {@link onConflict} resolver is wired (or non-interactive).\n * Default `'overwrite'` preserves the v1.2 behavior (every sync clobbers).\n * `'preserve'` skips the differing file (the CI / non-TTY default the CLI\n * threads via `buildConflictOpts`). */\n conflictPolicy?: 'overwrite' | 'preserve';\n /** Per-file conflict resolver. The CLI passes its single\n * `buildConflictOpts().onConflict` (clack menu + diff preview + apply-to-\n * all) through unchanged; structurally compatible with @noir-ai/create's\n * `ConflictResolverReturn`. */\n onConflict?: SkillConflictResolver;\n /** Explicit interactivity flag (the engine reads THIS, not process.env,\n * matching @noir-ai/create's hermetic-interactive contract). When `false`,\n * the resolver is NEVER consulted (CI / --json never hangs on a prompt). */\n interactive?: boolean;\n } = {},\n): Promise<EmitSummary> {\n const target: CompileTarget = opts.target ?? 'claude';\n const { builtins, integrations } = discoverAll({\n builtinDir: opts.builtinDir,\n integrationsDir: opts.integrationsDir,\n });\n const emitIntegrations = opts.includeIntegrations ?? true;\n // Validate the whole pack before writing anything (fail-fast, atomic-ish).\n for (const s of builtins) {\n const res = validateSkill(s);\n if (!res.ok) throw new Error(`Invalid builtin skill ${s.name}: ${res.errors.join('; ')}`);\n }\n if (emitIntegrations) {\n for (const i of integrations) {\n const res = validateSkill(i);\n if (!res.ok) throw new Error(`Invalid integration ${i.name}: ${res.errors.join('; ')}`);\n }\n }\n await mkdir(targetDir, { recursive: true });\n let references = 0;\n const emitted: string[] = [];\n const integrationNames: string[] = [];\n const conflicts: SkillConflict[] = [];\n // Apply-to-all memory keyed by artifact CLASS. Skill emission shares one\n // decision across the run (a `noir sync` touching 8 skill files → 1 prompt)\n // when the resolver returns `{resolution, applyToAll: true}`.\n const memory = new Map<string, SkillConflictResolution>();\n const policy: 'overwrite' | 'preserve' = opts.conflictPolicy ?? 'overwrite';\n // Non-interactive guard. The bridge `NOIR_NON_INTERACTIVE` is set by the\n // bin's preAction under --json/--no-input; the explicit `interactive: false`\n // wins over the env (mirrors @noir-ai/create's hermetic flag). When false,\n // the resolver is NEVER consulted — differing files fall back to `policy`.\n const interactive =\n opts.interactive ??\n (process.env.NOIR_NON_INTERACTIVE === undefined || process.env.NOIR_NON_INTERACTIVE === '');\n\n /** Write a compiled skill file through the SAME conflict seam as\n * `regenerate`: read existing; if differs, consult the resolver (when\n * interactive + wired); record the conflict for the structured report. */\n const writeWithConflict = async (\n absDest: string,\n relDest: string,\n content: string,\n isReference: boolean,\n ): Promise<void> => {\n let existing: string | undefined;\n try {\n existing = await readFile(absDest, 'utf8');\n } catch {\n existing = undefined;\n }\n if (existing !== undefined && existing !== content) {\n const MODE = 'skill' as const;\n let resolution: SkillConflictResolution;\n const remembered = memory.get(MODE);\n if (remembered !== undefined) {\n resolution = remembered;\n } else if (interactive && opts.onConflict !== undefined) {\n const ctx: SkillConflictContext = {\n relPath: relDest,\n existing,\n proposed: content,\n mode: MODE,\n };\n const ret = await opts.onConflict(ctx);\n const unwrapped: SkillConflictResolution = typeof ret === 'string' ? ret : ret.resolution;\n if (typeof ret !== 'string' && ret.applyToAll === true) {\n memory.set(MODE, unwrapped);\n }\n resolution = unwrapped;\n } else {\n resolution = policy === 'preserve' ? 'preserve' : 'replace';\n }\n conflicts.push({\n path: relDest,\n mode: MODE,\n similarity: similarity(existing, content),\n existingSha: sha256Hex12(existing),\n proposedSha: sha256Hex12(content),\n resolution,\n });\n if (resolution === 'cancel') {\n throw new Error(`skill emit cancelled by user at ${relDest}`);\n }\n if (resolution === 'preserve') {\n // Skip this file; the user's bytes stand. Do not count as a reference.\n return;\n }\n if (resolution === 'rename') {\n // Move the user's file aside (unique suffix), then write the compiled bytes.\n await renameAside(absDest, '.local');\n } else if (resolution === 'duplicate') {\n // Write the compiled bytes to <absDest>.noir; keep the user's file.\n await writeFile(await uniqueAside(absDest, '.noir'), content, 'utf8');\n return;\n }\n // 'replace' → fall through and overwrite.\n }\n await writeFile(absDest, content, 'utf8');\n if (isReference) references++;\n };\n\n // Cursor skills land FLAT under targetDir (`.cursor/rules/<name>.mdc`) —\n // Cursor's rule loader scans `.cursor/rules/*.mdc` and does NOT recurse into\n // per-name subdirs. The verbatim branch (claude/agents-md/gemini/opencode)\n // keeps the canonical nested layout (`<name>/SKILL.md` + `<name>/references/`).\n const flat = target === 'cursor';\n for (const s of builtins) {\n const compiled = compileSkill(s, target);\n for (const f of compiled.files) {\n const dest = flat ? join(targetDir, ...f.path) : join(targetDir, s.name, ...f.path);\n await mkdir(dirname(dest), { recursive: true });\n const rel = flat ? f.path.join('/') : [s.name, ...f.path].join('/');\n await writeWithConflict(dest, rel, f.content, f.path[0] !== 'SKILL.md');\n }\n emitted.push(s.name);\n }\n if (emitIntegrations) {\n for (const i of integrations) {\n const compiled = compileIntegration(i, target);\n for (const f of compiled.files) {\n const dest = flat ? join(targetDir, ...f.path) : join(targetDir, i.name, ...f.path);\n await mkdir(dirname(dest), { recursive: true });\n const rel = flat ? f.path.join('/') : [i.name, ...f.path].join('/');\n await writeWithConflict(dest, rel, f.content, f.path[0] !== 'SKILL.md');\n }\n // `hostMcp` is NOT written per-skill — it is surfaced to the host adapter\n // (via discoverAll/compileIntegration) to merge into the host's single\n // MCP config (e.g. `.mcp.json`). Wiring lives in cli/daemon.\n emitted.push(i.name);\n integrationNames.push(i.name);\n }\n }\n\n // --- Prune stale `noir-*` entries from the managed namespace ----------\n // Idempotent hygiene: a previous Noir version may have shipped a builtin that\n // was since renamed/removed (e.g. `noir-old-thing`). Each `noir sync`\n // re-writes the CURRENT pack but a stale entry would otherwise linger forever.\n // After emit, scan `targetDir` for `noir-`-prefixed entries NOT in the emitted\n // set and remove them. ONLY the `noir-` namespace — user skills without the\n // prefix are NEVER touched (they are not Noir's to manage).\n //\n // `assertNotUserOwned` guard: a `noir-*` entry whose content does NOT\n // match the canonical Noir-emitted shape (SKILL.md with `name: noir-…`\n // frontmatter, OR a `.mdc` with the cursor frontmatter) is treated as\n // USER-AUTHORED and LEFT ALONE. A user hand-rolling `noir-myown/SKILL.md`\n // must never be silently deleted by a sync.\n //\n // Shape awareness: the nested layout (claude/agents-md/gemini/opencode)\n // writes one `noir-<name>/` DIR per skill → prune stale DIRS. The cursor flat\n // layout writes one `noir-<name>.mdc` FILE per skill → prune stale .mdc FILES.\n // Cursor ALSO clears legacy `noir-<name>/` dirs (nesting residue) so an\n // upgrade from nested→flat does not leave orphans under `.cursor/rules/`.\n const keep = new Set(emitted);\n const pruned: string[] = [];\n const preservedUserOwned: string[] = [];\n let dirEntries: import('node:fs').Dirent[] = [];\n try {\n dirEntries = await readdir(targetDir, { withFileTypes: true });\n } catch {\n dirEntries = []; // targetDir vanished between the mkdir above and now — nothing to prune.\n }\n for (const ent of dirEntries) {\n if (!ent.name.startsWith('noir-')) continue;\n if (flat) {\n // Cursor flat layout — prune stale FILES (`noir-<name>.mdc`) + legacy\n // nested DIRS (`noir-<name>/`) from a sync.\n if (ent.isFile()) {\n const mdcMatch = ent.name.match(/^(noir-[a-z0-9]+(?:-[a-z0-9]+)*)\\.mdc$/);\n const skillName = mdcMatch?.[1];\n if (skillName && keep.has(skillName)) continue;\n // `assertNotUserOwned`: a `.mdc` without canonical cursor\n // frontmatter (globs + alwaysApply) was hand-rolled by the user; skip.\n if (await isUserOwnedMdc(join(targetDir, ent.name))) {\n preservedUserOwned.push(ent.name);\n continue;\n }\n } else if (ent.isDirectory()) {\n // Legacy cursor nested dir — clear unconditionally (the flat\n // layout has NO `noir-{name}/` dirs under .cursor/rules/; any such dir\n // is stale by definition, regardless of name overlap with the current\n // pack — the fresh `.mdc` file is what's kept, not the dir).\n // Guard: skip if NONE of the `.mdc` files inside look Noir-emitted\n // (canonical cursor frontmatter) — a user hand-rolling a noir-X/ dir\n // with arbitrary content is preserved.\n if (await isUserOwnedCursorLegacyDir(join(targetDir, ent.name))) {\n preservedUserOwned.push(ent.name);\n continue;\n }\n } else {\n continue;\n }\n } else {\n // Nested layout — prune stale DIRS only (the canonical shape).\n if (!ent.isDirectory()) continue;\n if (keep.has(ent.name)) continue;\n // `assertNotUserOwned`: a `noir-*/` dir whose SKILL.md does not\n // carry canonical Noir frontmatter (`name: noir-…`) is user-authored.\n if (await isUserOwnedSkillDir(join(targetDir, ent.name))) {\n preservedUserOwned.push(ent.name);\n continue;\n }\n }\n // Best-effort removal: a failure to remove a stale entry must NOT fail the\n // emit (the fresh skills are already on disk and valid). Surface it via the\n // summary so a caller can warn; the next sync will try again.\n try {\n await rm(join(targetDir, ent.name), { recursive: true, force: true });\n pruned.push(ent.name);\n } catch {\n // Swallow — stale-entry pruning is hygienic, not correctness-critical.\n }\n }\n\n return {\n dir: targetDir,\n emitted,\n references,\n integrations: integrationNames,\n pruned,\n ...(preservedUserOwned.length > 0 ? { preservedUserOwned } : {}),\n ...(conflicts.length > 0 ? { conflicts } : {}),\n };\n}\n\n/** `assertNotUserOwned` for the nested layout: a `noir-{name}/` dir is\n * Noir-managed iff it contains a SKILL.md whose YAML frontmatter carries a\n * `name:` matching `noir-<dir>`. Missing SKILL.md, missing frontmatter, or a\n * non-matching name ⇒ user-authored (left alone). Best-effort: any IO error\n * ⇒ treat as user-owned (the safe default — never silently delete). */\nasync function isUserOwnedSkillDir(dirAbs: string): Promise<boolean> {\n try {\n const skillMd = await readFile(join(dirAbs, 'SKILL.md'), 'utf8');\n const m = skillMd.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!m) return true; // no frontmatter at all → user-authored\n const fm = parseYaml(m[1] ?? '') as { name?: unknown };\n const dirName = basename(dirAbs);\n return typeof fm?.name !== 'string' || fm.name !== dirName;\n } catch {\n return true; // ENOENT (no SKILL.md) or parse error → user-authored (safe)\n }\n}\n\n/** `assertNotUserOwned` for the cursor flat layout: a `noir-*.mdc` file\n * is Noir-managed iff its YAML frontmatter carries `alwaysApply:` (the cursor\n * shape the compiler always emits). Missing frontmatter / no `alwaysApply` ⇒\n * user-authored. */\nasync function isUserOwnedMdc(fileAbs: string): Promise<boolean> {\n try {\n const md = await readFile(fileAbs, 'utf8');\n const m = md.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n if (!m) return true;\n const fm = parseYaml(m[1] ?? '') as { alwaysApply?: unknown };\n return fm?.alwaysApply === undefined;\n } catch {\n return true;\n }\n}\n\n/** `assertNotUserOwned` for the cursor flat-layout's legacy-nested-dir\n * case: a Noir sync left `noir-{name}/noir-{name}.mdc` behind. The dir\n * is Noir-managed iff at least one `.mdc` inside carries canonical cursor\n * frontmatter (`alwaysApply:`). Absent / hand-rolled content ⇒ user-authored.\n * Empty dir ⇒ treat as user-owned (safe — never silently delete). */\nasync function isUserOwnedCursorLegacyDir(dirAbs: string): Promise<boolean> {\n try {\n const entries = await readdir(dirAbs);\n if (entries.length === 0) return true;\n let sawCanonical = false;\n for (const name of entries) {\n if (!name.endsWith('.mdc')) continue;\n if (!(await isUserOwnedMdc(join(dirAbs, name)))) {\n sawCanonical = true;\n break;\n }\n }\n return !sawCanonical;\n } catch {\n return true; // ENOENT or unreadable → safe default (preserve)\n }\n}\n\n/** LCS similarity in [0,1]. Local copy (skills has no create dependency)\n * so the structured report's `similarity` matches the engine's algorithm. */\nfunction similarity(a: string, b: string): number {\n if (a === b) return 1;\n const A = a.split('\\n');\n const B = b.split('\\n');\n if (A.length === 0 && B.length === 0) return 1;\n const n = A.length;\n const m = B.length;\n const dp: number[][] = Array.from({ length: n + 1 }, () => new Array<number>(m + 1).fill(0));\n for (let i = n - 1; i >= 0; i--) {\n const dpi = dp[i] ?? [];\n const dpi1 = dp[i + 1] ?? [];\n const ai = A[i] ?? '';\n for (let j = m - 1; j >= 0; j--) {\n const bj = B[j] ?? '';\n dpi[j] = ai === bj ? (dpi1[j + 1] ?? 0) + 1 : Math.max(dpi1[j] ?? 0, dpi[j + 1] ?? 0);\n }\n }\n const lcs = dp[0]?.[0] ?? 0;\n return n + m === 0 ? 1 : (2 * lcs) / (n + m);\n}\n\n/** Pick a fresh `<abs><suffix>` aside path that does NOT exist (mirrors\n * @noir-ai/create's `uniqueAside`). Used by the `rename`/`duplicate` resolutions\n * so the user's bytes never get silently clobbered. */\nasync function renameAside(abs: string, suffix: string): Promise<void> {\n const { rename } = await import('node:fs/promises');\n const dest = await uniqueAside(abs, suffix);\n await rename(abs, dest);\n}\n\nasync function uniqueAside(abs: string, suffix: string): Promise<string> {\n return uniqueAsideSync(abs, suffix);\n}\n\n// Convenience for callers/tests that already hold raw markdown (unused by emit path;\n// kept so adapters/tests can validate a single in-memory skill without a dir).\nexport { discoverAll, discoverBuiltin };\n","import { readdirSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseFrontmatter } from './compiler.js';\nimport { parseIntegration } from './integrations-schema.js';\nimport type { BuiltinSkill, IntegrationDeclaration, IntegrationSkill } from './types.js';\n\n// Package root = ONE level up from THIS module's directory. Works in every layout\n// because `builtin/` is always a sibling of the dir this code runs from:\n// - vitest: .../packages/skills/src/discover.ts -> HERE = src -> PKG_ROOT = package root\n// - built: .../packages/skills/dist/index.js -> HERE = dist -> PKG_ROOT = package root\n// (tsup bundles everything into dist/index.js; `dirname` still yields `dist`.)\nconst HERE = dirname(fileURLToPath(import.meta.url)); // .../src OR .../dist\nconst PKG_ROOT = dirname(HERE); // .../packages/skills\nexport const BUILTIN_DIR = join(PKG_ROOT, 'builtin');\nexport const INTEGRATIONS_DIR = join(PKG_ROOT, 'integrations');\n\n/** Read a `<name>/SKILL.md` + its optional `references/` directory. Shared by\n * builtin + integration discovery (both share the SKILL.md + references shape;\n * the difference is the `integration.json` declaration). */\nfunction readSkillMd(dir: string): Pick<BuiltinSkill, 'skillMd' | 'frontmatter' | 'references'> {\n const skillMd = readFileSync(join(dir, 'SKILL.md'), 'utf8');\n const frontmatter = parseFrontmatter(skillMd);\n const refsDir = join(dir, 'references');\n let references: BuiltinSkill['references'] = [];\n try {\n references = readdirSync(refsDir)\n .filter((f) => f.endsWith('.md'))\n .sort()\n .map((f) => ({ name: f, content: readFileSync(join(refsDir, f), 'utf8') }));\n } catch {\n references = []; // no references/ dir is fine\n }\n return { skillMd, frontmatter, references };\n}\n\nexport function discoverBuiltin(builtinDir: string = BUILTIN_DIR): BuiltinSkill[] {\n const dirs = readdirSync(builtinDir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && e.name.startsWith('noir-'))\n .map((e) => e.name)\n .sort();\n return dirs.map((name) => {\n const dir = join(builtinDir, name);\n return { name, dir, ...readSkillMd(dir) };\n });\n}\n\n/**\n * Discover every shipped integration under `integrations/<name>/` (sibling of\n * `builtin/`). Each entry is a builtin-shaped skill (validated `SKILL.md` +\n * optional references) PLUS a parsed + validated `integration.json`\n * declaration. The dir is resolved relative to the package root by default —\n * when `integrationsDir` is overridden (tests), only that dir is scanned.\n *\n * Throws an aggregate error if any integration is malformed (missing\n * `SKILL.md`, bad frontmatter, or an `integration.json` that fails the Zod\n * schema) — fail-fast, atomic-ish, mirroring the builtin compiler's posture.\n */\nexport function discoverIntegrations(\n integrationsDir: string = INTEGRATIONS_DIR,\n): IntegrationSkill[] {\n let entries: string[];\n try {\n entries = readdirSync(integrationsDir, { withFileTypes: true })\n .filter((e) => e.isDirectory() && e.name.startsWith('noir-'))\n .map((e) => e.name)\n .sort();\n } catch {\n // No integrations/ dir (or unreadable) = no integrations shipped here.\n return [];\n }\n return entries.map((name) => {\n const dir = join(integrationsDir, name);\n const declarationRaw = JSON.parse(\n readFileSync(join(dir, 'integration.json'), 'utf8'),\n ) as unknown;\n let declaration: IntegrationDeclaration;\n try {\n declaration = parseIntegration(declarationRaw);\n } catch (err) {\n // ZodError → wrap with the integration's dir for an actionable message.\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Invalid integration.json in ${dir}: ${msg}`);\n }\n if (declaration.name !== name) {\n throw new Error(\n `Integration dir \"${name}\" must equal declaration name \"${declaration.name}\"`,\n );\n }\n return { name, dir, ...readSkillMd(dir), declaration, declarationRaw };\n });\n}\n\n/**\n * Discover the full shipped pack — builtins + integrations — in one call. Used\n * by `emitSkillsToDir` so `noir init`/`sync` emit BOTH (slice X goal). The\n * `integrationsDir` defaults to the sibling of `builtinDir` so a test fixture\n * that overrides only `builtinDir` cleanly resolves integrations to a\n * non-existent path (returns `[]`) instead of accidentally picking up the\n * shipped integrations.\n */\nexport function discoverAll(opts: { builtinDir?: string; integrationsDir?: string } = {}): {\n builtins: BuiltinSkill[];\n integrations: IntegrationSkill[];\n} {\n const builtinDir = opts.builtinDir ?? BUILTIN_DIR;\n const integrationsDir = opts.integrationsDir ?? join(dirname(builtinDir), 'integrations');\n return {\n builtins: discoverBuiltin(builtinDir),\n integrations: discoverIntegrations(integrationsDir),\n };\n}\n","// `integration.json` schema. Each integration ships a declaration\n// alongside its `SKILL.md` so the compiler can validate the auth/runtime/SDD\n// contract uniformly and so the host adapter knows whether to widen emission\n// (host MCP config) or stay skill-only.\n//\n// Doctrine (slice-x spec §architecture + v1x §4.4): skill-only by default;\n// `gated-write-proxy` for stateless writes routed through a Noir MCP tool\n// `mcp-stdio` for a Noir-spawned stateful runtime (deferred, gated on\n// keychain); `external-mcp` for a config-only pointer at a first-party/community\n// MCP server (emits a host `.mcp.json` server entry, no Noir runtime).\n//\n// Zod v4 (`z.record(keyType, valueType)` is key-first). The schema is additive\n// and defaults to the safest tier — every optional field degrades to \"skill\n// only, manual paste, no host MCP wiring\" — so a minimal `{ \"name\":\n// \"noir-x\" }` declaration still parses + behaves as a pure playbook.\n\nimport * as z from 'zod';\n\n/** Auth shape. Locked: `env-var` only until keychain lands (Q4b — refuse OAuth,\n * never silently lower the security bar). `fallback:'manual-paste'` keeps the\n * no-token path honest (the playbook tells the user to paste a value); `'none'`\n * is for integrations that genuinely do not need a token (read-only public\n * endpoints). */\nexport const IntegrationAuthSchema = z.object({\n type: z.literal('env-var'),\n tokenEnv: z.string().min(1),\n fallback: z.enum(['manual-paste', 'none']).default('manual-paste'),\n});\n\n/** SDD two-way binding. `intakeFrom` declares the external artifact kind the\n * `noir-brainstorming` skill pulls from (absorbed noir-intake in C3);\n * `writeBack` enumerates the fields `noir-wrap` pushes back at session end\n * (absorbed noir-document in C3). Strings (not enums) for `writeBack` so a\n * per-integration vocabulary stays expressible without\n * churning the schema. */\nexport const IntegrationSddSchema = z\n .object({\n intakeFrom: z.enum(['task', 'issue', 'none']).optional(),\n writeBack: z.array(z.string()).default([]),\n })\n .default({ writeBack: [] });\n\n/** Host MCP declaration. Only meaningful when `runtime ∈ {mcp-stdio,\n * external-mcp}`; `null` for `none`/`gated-write-proxy` (ClickUp — writes go\n * through Noir's own MCP tool, never the host's). */\nexport const IntegrationMcpSchema = z\n .object({\n command: z.string().min(1),\n transport: z.enum(['stdio', 'http']),\n args: z.array(z.string()).optional(),\n url: z.string().optional(),\n env: z.record(z.string(), z.string()).optional(),\n })\n .nullable()\n .default(null);\n\n/** Full `integration.json` declaration. `name` enforces the `noir-` prefix\n * shared with builtins so the unified pack reads consistently. */\nexport const IntegrationDeclarationSchema = z.object({\n name: z.string().regex(/^noir-[a-z0-9]+(?:-[a-z0-9]+)*$/, 'must match noir-<kebab>'),\n auth: IntegrationAuthSchema,\n runtime: z.enum(['none', 'gated-write-proxy', 'mcp-stdio', 'external-mcp']),\n sdd: IntegrationSddSchema,\n mcp: IntegrationMcpSchema,\n});\n\nexport type IntegrationDeclaration = z.infer<typeof IntegrationDeclarationSchema>;\n\n/** Coerce + validate an `integration.json` payload. Throws a ZodError on a\n * malformed declaration — the caller (discover) wraps it with the\n * integration's dir for an actionable message. */\nexport function parseIntegration(json: unknown): IntegrationDeclaration {\n return IntegrationDeclarationSchema.parse(json);\n}\n\n/** Non-throwing variant for the hygiene tests + lint helpers. */\nexport function validateIntegration(\n obj: unknown,\n): { ok: true; value: IntegrationDeclaration } | { ok: false; errors: string[] } {\n const parsed = IntegrationDeclarationSchema.safeParse(obj);\n if (parsed.success) return { ok: true, value: parsed.data };\n return { ok: false, errors: parsed.error.issues.map((i: { message: string }) => i.message) };\n}\n\n/** Runtime → host-MCP emission predicate. The compiler widens emission (makes\n * the host MCP block available to the adapter) ONLY for these tiers. The\n * adapter ultimately decides per-host what to render — for Claude, `external-\n * mcp` becomes a `.mcp.json` server entry; `mcp-stdio` registers through the\n * existing `noir mcp serve --stdio` entry so no NEW server entry is emitted. */\nexport function runtimeEmitsHostMcp(runtime: IntegrationDeclaration['runtime']): boolean {\n return runtime === 'mcp-stdio' || runtime === 'external-mcp';\n}\n","// Structural quality checks for the builtin skill pack — the C3 quality gate.\n//\n// Split from compiler.ts so `validateSkill` (hard errors) and `lintSkill` (soft\n// warnings) share one source of truth for the body-structure rules, and so the\n// rules are unit-testable in isolation. Canonical template (C3 spec §3):\n//\n// Overview → When to use → Procedure → Verification → Notes\n//\n// Rules here implement the researched canon (Anthropic best practices, the\n// agentskills.io spec, Claude Code docs): required sections, a <500-line body\n// budget, one-level-deep references (no chained refs), and a WHAT+WHEN\n// description. `checkRequiredSections` is the load-bearing check — it's what\n// forces every SKILL.md to carry a real playbook shape, not a shell.\n\nimport { ARTIFACT_TYPES } from '@noir-ai/core';\nimport type { BuiltinSkill } from './types.js';\n\n/** The max body length the canon recommends (Anthropic: \"under 500 lines\").\n * SKILL.md is a navigator, not a repository — split to references/ past this. */\nexport const MAX_BODY_LINES = 500;\n\n/** The floor for a full (non-stub) playbook body. Stubs were ~13 lines; a real\n * playbook needs at least this much substance to be loadable as guidance. */\nexport const MIN_FULL_BODY_LINES = 20;\n\n/** The canonical heading a skill MUST carry to describe its trigger conditions.\n * Accepts the C3 template spelling AND common variants so existing skills\n * aren't forced into a single casing. */\nconst WHEN_SECTION = /^## When to use$/im;\n\n/** The canonical heading a skill MUST carry for its numbered workflow. `## Steps`\n * is accepted for backward-compat with noir-wrap; `## Procedure` is canonical. */\nconst PROCEDURE_SECTION = /^## (Procedure|Steps)$/im;\n\n/** One of these closing sections must exist — evidence/notes/degradation. */\nconst CLOSING_SECTION = /^## (Verification|Notes|Fallbacks|Troubleshooting|Why order matters)$/im;\n\n/** The lead cue regex — the FIRST part of the description must be a trigger. */\nconst WHEN_CUE =\n /^(use|using|used|whenever|when|before|after|while|starting|encountering|completing|creating|about to|upon|during|to|for|on)\\b/i;\n\n/** A chained reference link — a reference file that links to ANOTHER reference\n * file (`references/a.md` → `references/b.md` or `../references/b.md`). The\n * canon forbids deep nesting (agentskills.io: \"no chained references\"). */\nconst CHAINED_REF_RE = /\\]\\((\\/?\\.?\\.?\\/)?(references\\/|\\.\\.\\/references\\/)/i;\n\n/**\n * Which required sections are missing from a skill body. Returns a list of\n * human-readable section names, e.g. `['## When to use', '## Verification']`.\n * Empty array = the body carries the canonical playbook shape.\n */\nexport function missingSections(body: string): string[] {\n const missing: string[] = [];\n if (!WHEN_SECTION.test(body)) missing.push('## When to use');\n if (!PROCEDURE_SECTION.test(body)) missing.push('## Procedure (or ## Steps)');\n if (!CLOSING_SECTION.test(body)) {\n missing.push('one of ## Verification / ## Notes / ## Fallbacks / ## Troubleshooting');\n }\n return missing;\n}\n\n/** True when the body is within the canon line budget. */\nexport function withinLineBudget(body: string, max: number = MAX_BODY_LINES): boolean {\n return body.split('\\n').length <= max;\n}\n\n/** Reference files that point at another reference file (chained — forbidden).\n * Returns the offending reference names. */\nexport function chainedReferences(skill: BuiltinSkill): string[] {\n return skill.references.filter((r) => CHAINED_REF_RE.test(r.content)).map((r) => r.name);\n}\n\n/**\n * True when the description carries BOTH a WHEN trigger lead AND a WHAT clause.\n * The C3 rule: `description` MUST lead with a WHEN cue (existing compiler rule)\n * AND contain a compact WHAT clause naming what the skill does.\n *\n * Two canonical shapes both pass:\n * \"Use when turning an idea into a spec — draft the spec.\" (WHEN → WHAT)\n * \"Use when a task writes back to ClickUp: update its status.\" (WHEN → WHAT)\n * The description MUST lead with a cue (the WHEN-only and WHAT-only schools are\n * both rejected). The WHAT clause is the part after the trigger phrase — split\n * on an em/en dash or a period that ends the trigger phrase. A real WHAT clause\n * has ≥3 words naming the action, so a bare \"Use when…\" fails.\n */\nexport function isWhatWhenDescription(description: string): boolean {\n const trimmed = description.trim();\n if (!trimmed) return false;\n if (!WHEN_CUE.test(trimmed)) return false; // must lead with a cue\n // Split off the trigger phrase: after an em/en dash, or after the first period.\n // \"Use when X — draft the spec.\" → whatPart = \"draft the spec.\"\n const whatPart = trimmed\n .split(/[—–]|(?<=\\.) /)\n .slice(1)\n .join(' ')\n .trim();\n return whatPart.split(/\\s+/).filter(Boolean).length >= 3;\n}\n\n/** True when the description leads with a WHEN cue (the existing rule, kept\n * here so validate + lint + hygiene share one implementation). */\nexport function looksLikeWhenDescription(description: string): boolean {\n return WHEN_CUE.test(description.trim());\n}\n\n/**\n * Soft-quality warnings for `lintSkill`. Each returns a short rule id + message.\n * These are advisory — a skill can pass `validateSkill` and still carry lint\n * warnings that the author should resolve. Rules are drawn from the researched\n * anti-pattern list (no examples, thin body, first/second person narration,\n * voodoo constants, time-sensitive version pins).\n */\nexport function lintWarnings(skill: BuiltinSkill): string[] {\n const warnings: string[] = [];\n const body = skill.skillMd; // linting the whole file (frontmatter + body) is fine for prose checks\n const bodyLines = body.split('\\n').length;\n\n if (bodyLines < MIN_FULL_BODY_LINES + 6) {\n // frontmatter is ~6 lines; a full playbook should clear 20 body lines\n warnings.push('thin-body: full playbook body is under 20 lines');\n }\n // Concrete examples: at least one fenced block or an \"example:\" mention.\n const hasExample = /```/.test(body) || /\\bexample:?\\b/i.test(body) || /\\be\\.g\\.\\b/i.test(body);\n if (!hasExample) {\n warnings.push('no-example: no concrete code fence or worked example in the body');\n }\n // First/second-person narration — \"I/we/you\" as the agent doing work.\n if (\n /\\b(I|we|you)\\s+(will|should|can|need|must|do|write|create|implement|run|build)\\b/i.test(body)\n ) {\n warnings.push('first-person: narration addresses the reader instead of imperative steps');\n }\n // Voodoo constants: a bare number with a magnitude claim but no justification\n // (\"wait 5 seconds\" without why). Very loose — catches \"wait N seconds\" without\n // a \"why\".\n if (\n /\\b(?:wait|sleep|retry|backoff|limit|cap)\\s+[a-z]*\\s*(\\d{1,4})\\b/i.test(body) &&\n !/because|to (avoid|prevent|give|let)/i.test(body)\n ) {\n warnings.push('voodoo-constant: numeric threshold without a stated reason');\n }\n // Time-sensitive version pins outside a Legacy section.\n const hasVersionPin = /as of \\d{4}|\\bversion \\d+\\.\\d+\\.\\d+\\b|\"v\\d+\\.\\d+\"/i.test(body);\n const hasLegacySection = /^## Legacy|^## Old patterns/i.test(body);\n if (hasVersionPin && !hasLegacySection) {\n warnings.push('time-sensitive: version/date pin outside a Legacy section');\n }\n return warnings;\n}\n\n/** `.noir/` subdirectories a skill may legitimately reference that are NOT\n * per-type artifact dirs (store db, audit export, rules, daemon state). */\nconst NON_ARTIFACT_DIRS = new Set(['store', 'audit', 'rules', 'state']);\n\n/** Canonical artifact directory → its type code(s) (from the C3 registry).\n * One directory can host multiple kinds (`subagents/` → BR + RP). */\nconst DIR_TO_CODES: ReadonlyMap<string, ReadonlySet<string>> = (() => {\n const m = new Map<string, Set<string>>();\n for (const t of Object.values(ARTIFACT_TYPES)) {\n const codes = m.get(t.dir);\n if (codes) codes.add(t.code);\n else m.set(t.dir, new Set([t.code]));\n }\n return m;\n})();\n\n/**\n * `.noir/…` output-path drift: a skill body or reference that names a `.noir/`\n * directory absent from the artifact registry (e.g. `.noir/sdd/`), or a file\n * under a canonical artifact directory whose name carries none of that\n * directory's type codes (e.g. `.noir/plans/<date>-<slug>.md` instead of\n * `PL-<NNNN>-…`). Hard errors — the host would mint non-standard artifacts.\n * See `docs/reference/artifact-format.md`.\n */\nexport function artifactPathDrift(skill: BuiltinSkill): string[] {\n const texts = [skill.skillMd, ...skill.references.map((r) => r.content)];\n const drifts = new Set<string>();\n // `.noir/<dir>/<name>` — name runs to whitespace/backtick/quote/paren so\n // `<date>-<slug>.md` placeholders are captured whole.\n const re = /\\.noir\\/([a-zA-Z0-9-]+)\\/([^\\s`'\"()]+)/g;\n for (const text of texts) {\n for (const m of text.matchAll(re)) {\n const dir = m[1];\n const name = m[2];\n if (!dir || !name) continue;\n const codes = DIR_TO_CODES.get(dir);\n if (codes === undefined) {\n if (!NON_ARTIFACT_DIRS.has(dir)) {\n drifts.add(`.noir/${dir}/ is not a canonical artifact directory (C3 artifact standard)`);\n }\n continue;\n }\n const expected = [...codes].join('/');\n if (![...codes].some((c) => name.startsWith(`${c}-`))) {\n drifts.add(`.noir/${dir}/${name} must be ${expected}-<NNNN>-… (C3 artifact standard)`);\n }\n }\n }\n return [...drifts];\n}\n","// Offline behavioral-eval harness for the skill pack — C3.\n//\n// Skills are declared as `evals/**/evals.json` per the agentskills.io format\n// (`{skill_name, evals:[{id, prompt, expected_output, assertions?}]}`). The\n// harness loads every suite and runs OFFLINE assertions only — `contains`,\n// `not-contains`, `regex`, `length-gte` — no LLM, no network, so it runs in\n// `pnpm test` (the project's offline/free constraint). This is the executable\n// that turns the JSON declaration into a vitest suite (see test/evals.test.ts).\n//\n// The evals are deliberately simple: they assert that a skill's PROMPT → a\n// simulated agent answer satisfies structural expectations. They are NOT\n// LLM-judge evals (deferred to a future slice, ADR'd); they are the CI-safe\n// baseline that catches regressions in the skill's core directives.\n\nimport { readdirSync, readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/** A single offline assertion on the produced output. */\nexport type EvalAssertion =\n | { type: 'contains'; value: string }\n | { type: 'not-contains'; value: string }\n | { type: 'regex'; value: string }\n | { type: 'length-gte'; value: number };\n\n/** One eval case: a prompt + the expected directive the skill must produce. */\nexport interface SkillEval {\n id: string;\n prompt: string;\n expected_output: string;\n assertions?: EvalAssertion[];\n}\n\n/** A suite = one skill's evals (the `evals.json` shape). */\nexport interface EvalSuite {\n skill_name: string;\n evals: SkillEval[];\n}\n\n/** Validate an assertion's shape (fail-fast on a malformed evals.json). */\nfunction parseAssertion(a: unknown): EvalAssertion {\n if (typeof a !== 'object' || a === null) throw new Error('assertion must be an object');\n const { type, value } = a as { type?: unknown; value?: unknown };\n switch (type) {\n case 'contains':\n case 'not-contains':\n case 'regex':\n if (typeof value !== 'string' || value.length === 0) {\n throw new Error(`assertion '${String(type)}' requires a non-empty string value`);\n }\n return { type, value } as EvalAssertion;\n case 'length-gte':\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new Error(\"assertion 'length-gte' requires a finite number value\");\n }\n return { type, value } as EvalAssertion;\n default:\n throw new Error(`unknown assertion type '${String(type)}'`);\n }\n}\n\n/** Parse + validate a raw `evals.json` payload. Throws on malformed shape. */\nexport function parseEvalSuite(raw: unknown): EvalSuite {\n if (typeof raw !== 'object' || raw === null) throw new Error('evals.json must be an object');\n const { skill_name, evals } = raw as { skill_name?: unknown; evals?: unknown };\n if (typeof skill_name !== 'string' || skill_name.length === 0) {\n throw new Error('evals.json requires a non-empty string `skill_name`');\n }\n if (!Array.isArray(evals) || evals.length === 0) {\n throw new Error(`evals.json for ${skill_name} requires a non-empty 'evals' array`);\n }\n const parsed: SkillEval[] = evals.map((e, i) => {\n if (typeof e !== 'object' || e === null) throw new Error(`eval[${i}] must be an object`);\n const { id, prompt, expected_output, assertions } = e as {\n id?: unknown;\n prompt?: unknown;\n expected_output?: unknown;\n assertions?: unknown;\n };\n if (typeof id !== 'string' || id.length === 0)\n throw new Error(`eval[${i}] requires a string id`);\n if (typeof prompt !== 'string' || prompt.length === 0) {\n throw new Error(`eval ${id} requires a string prompt`);\n }\n if (typeof expected_output !== 'string') {\n throw new Error(`eval ${id} requires a string expected_output`);\n }\n return {\n id,\n prompt,\n expected_output,\n assertions: Array.isArray(assertions) ? assertions.map(parseAssertion) : undefined,\n };\n });\n return { skill_name, evals: parsed };\n}\n\n/** Run a list of assertions against `output`. Returns pass + per-assertion failures. */\nexport function runAssertions(\n output: string,\n assertions: EvalAssertion[],\n): { pass: boolean; failures: string[] } {\n const failures: string[] = [];\n for (const a of assertions) {\n switch (a.type) {\n case 'contains':\n if (!output.includes(a.value)) failures.push(`expected to contain \"${a.value}\"`);\n break;\n case 'not-contains':\n if (output.includes(a.value)) failures.push(`expected NOT to contain \"${a.value}\"`);\n break;\n case 'regex':\n if (!new RegExp(a.value, 'i').test(output)) failures.push(`expected to match /${a.value}/`);\n break;\n case 'length-gte':\n if (output.length < a.value)\n failures.push(`expected length >= ${a.value} (got ${output.length})`);\n break;\n }\n }\n return { pass: failures.length === 0, failures };\n}\n\n// Package root (evals/ is a sibling of src/): resolve the evals dir the same\n// way discover.ts resolves builtin/. Works under vitest (src) + built (dist).\nconst HERE = dirname(fileURLToPath(import.meta.url));\nconst PKG_ROOT = dirname(HERE);\nexport const EVALS_DIR = join(PKG_ROOT, 'evals');\n\n/**\n * Load every eval suite under `dir` (default the shipped `evals/` dir — each\n * subdir carries an `evals.json`). Skips non-`.json` files and missing dirs\n * (no evals = no-op). Fail-fast on a malformed suite (a broken evals.json is a\n * bug, not a skip).\n */\nexport function loadEvalSuites(dir: string = EVALS_DIR): EvalSuite[] {\n let entries: string[];\n try {\n entries = readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort();\n } catch {\n return []; // no evals/ dir (or unreadable) = no suites shipped\n }\n const suites: EvalSuite[] = [];\n for (const name of entries) {\n const file = join(dir, name, 'evals.json');\n try {\n const raw = JSON.parse(readFileSync(file, 'utf8')) as unknown;\n suites.push(parseEvalSuite(raw));\n } catch (err) {\n // Missing evals.json in a subdir is fine (it may be a scratch dir); a\n // malformed one is not. Distinguish by existence.\n try {\n readFileSync(file, 'utf8');\n throw err; // exists but malformed → rethrow\n } catch (re) {\n if (re === err) throw err;\n // ENOENT → skip silently.\n }\n }\n }\n return suites;\n}\n\n/** Evaluate one suite: for each eval, check its assertions against\n * `expected_output` (the directive the skill should produce). Returns pass/fail\n * per eval with the assertion failures. This is the offline core the vitest\n * runner drives. */\nexport function evaluateSuite(\n suite: EvalSuite,\n): Array<{ id: string; pass: boolean; failures: string[] }> {\n return suite.evals.map((e) => ({\n id: e.id,\n pass: e.assertions ? runAssertions(e.expected_output, e.assertions).pass : true,\n failures: e.assertions ? runAssertions(e.expected_output, e.assertions).failures : [],\n }));\n}\n","// Runtime-derived skill registry — C3.\n//\n// The registry is NOT a committed file: frontmatter is the single source of\n// truth and `buildRegistry()` derives a queryable index from `discoverAll()`\n// on demand (the CLI calls it; no drift risk, per the C3 decision D3). Every\n// field comes from either frontmatter (`metadata.category`, `metadata.version`)\n// or a shape check (`status` from the stub marker, `referenceCount`/`lines`\n// from the discovered files).\n\nimport { discoverAll } from './discover.js';\nimport type { BuiltinSkill } from './types.js';\n\n/** The pack-wide `noir-` namespace a registry entry always belongs to. */\nexport const NOIR_NAMESPACE = 'noir-';\n\n/** One row in the derived registry. */\nexport interface SkillRegistryEntry {\n /** Canonical `noir-<kebab>` id (== dir name). */\n name: string;\n /** `builtin` for the shipped pack; `integration` for an `integrations/<name>/`. */\n kind: 'builtin' | 'integration';\n /** Category from `metadata.category`; falls back to the `noir-`-stripped name\n * (a newly-authored skill still gets a sensible cell). */\n category: string;\n /** Per-skill version from `metadata.version`; defaults to `0.0.0` when absent\n * (the pack-level version in package.json is the authoritative release). */\n version: string;\n /** `full` when the body has no `> **Stub:**` marker; `stub` otherwise. */\n status: 'full' | 'stub';\n /** Lifecycle stage of the skill. `active` = currently shipped and usable;\n * `deprecated` = superseded (a renamed/merged predecessor). C3 spec §8.\n * The curated pack has no deprecated members today. */\n lifecycle: 'active' | 'deprecated';\n /** The WHAT+WHEN description (the trigger the host sees). */\n description: string;\n /** Number of `references/*.md` files. */\n referenceCount: number;\n /** Total SKILL.md line count (frontmatter + body). */\n lines: number;\n}\n\n/** Derive the category from the skill name when `metadata.category` is absent. */\nfunction fallbackCategory(name: string): string {\n return name.replace(/^noir-/, '') || 'general';\n}\n\n/** Map a discovered builtin/integration to a registry row. */\nfunction toEntry(s: BuiltinSkill, kind: 'builtin' | 'integration'): SkillRegistryEntry {\n const category = s.frontmatter.metadata?.category?.trim() || fallbackCategory(s.name);\n const version = s.frontmatter.metadata?.version?.trim() || '0.0.0';\n const status: 'full' | 'stub' = s.skillMd.includes('> **Stub:**') ? 'stub' : 'full';\n const lines = s.skillMd.split('\\n').length;\n return {\n name: s.name,\n kind,\n category,\n version,\n status,\n // Every shipped skill is active; `deprecated` is reserved for a future\n // superseded member (C3 spec §8).\n lifecycle: 'active' as const,\n description: typeof s.frontmatter.description === 'string' ? s.frontmatter.description : '',\n referenceCount: s.references.length,\n lines,\n };\n}\n\n/**\n * Build the full registry from `discoverAll()` — builtins + integrations in\n * one list, sorted by name. Pure (no I/O beyond discovery); never throws for\n * a malformed skill (a discovery failure surfaces upstream, the same as the\n * compiler's fail-fast).\n */\nexport function buildRegistry(): SkillRegistryEntry[] {\n const { builtins, integrations } = discoverAll();\n return [\n ...builtins.map((b) => toEntry(b, 'builtin')),\n ...integrations.map((i) => toEntry(i, 'integration')),\n ].sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/** Convenience: registry filtered to a single category (CLI grouping). */\nexport function registryByCategory(category: string): SkillRegistryEntry[] {\n return buildRegistry().filter((e) => e.category === category);\n}\n","// Tokens that must NOT survive a port (predecessor plugin internals + Superpowers\n// rhetoric). Shared by the compiler's lint helper and the hygiene tests (T5).\nexport const FORBIDDEN_RESIDUE: readonly string[] = [\n 'workflow/<task', // predecessor state file\n 'noir-workflow.mode', // predecessor mode flag\n 'noir-workflow', // predecessor plugin name (as a plugin/path reference)\n '@uiigateway', // predecessor Angular specifics\n // NOTE: 'ClickUp'/'clickup' were forbidden during the predecessor-port era (the\n // ClickUp REST precedent lived in the deleted noir-workflow plugin). ClickUp is\n // now reintroduced as a first-class Noir integration (skills/integrations/noir-clickup),\n // so the token is no longer residue — it is the integration's legitimate subject.\n '<EXTREMELY-IMPORTANT', // Superpowers rhetoric\n 'SUBAGENT-STOP', // Superpowers rhetoric\n 'plugins/noir-workflow', // predecessor path\n];\n"],"mappings":"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","names":["dirname","join","join","dirname","readdirSync","readFileSync","dirname","join","fileURLToPath","HERE","PKG_ROOT"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noir-ai/skills",
3
- "version": "1.11.2-beta.1",
3
+ "version": "1.12.0-beta.1",
4
4
  "description": "Noir skills — the builtin noir-* skill pack, the integrations pack (ClickUp first), and a copy-and-validate compiler.",
5
5
  "license": "MIT",
6
6
  "author": "agaaaptr",
@@ -48,10 +48,10 @@
48
48
  "dependencies": {
49
49
  "yaml": "^2.5.0",
50
50
  "zod": "^4.2.0",
51
- "@noir-ai/core": "1.11.2-beta.1"
51
+ "@noir-ai/core": "1.12.0-beta.1"
52
52
  },
53
53
  "devDependencies": {
54
- "@types/node": "^26.1.1"
54
+ "@types/node": "^22.17.0"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsup",