@mariozechner/pi-coding-agent 0.27.9 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +16 -17
  3. package/dist/cli/list-models.d.ts +2 -2
  4. package/dist/cli/list-models.d.ts.map +1 -1
  5. package/dist/cli/list-models.js +2 -7
  6. package/dist/cli/list-models.js.map +1 -1
  7. package/dist/config.d.ts +2 -2
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +3 -3
  10. package/dist/config.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +6 -3
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +18 -20
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/auth-storage.d.ts +104 -0
  16. package/dist/core/auth-storage.d.ts.map +1 -0
  17. package/dist/core/auth-storage.js +232 -0
  18. package/dist/core/auth-storage.js.map +1 -0
  19. package/dist/core/model-registry.d.ts +50 -0
  20. package/dist/core/model-registry.d.ts.map +1 -0
  21. package/dist/core/model-registry.js +268 -0
  22. package/dist/core/model-registry.js.map +1 -0
  23. package/dist/core/model-resolver.d.ts +7 -7
  24. package/dist/core/model-resolver.d.ts.map +1 -1
  25. package/dist/core/model-resolver.js +12 -44
  26. package/dist/core/model-resolver.js.map +1 -1
  27. package/dist/core/sdk.d.ts +13 -26
  28. package/dist/core/sdk.d.ts.map +1 -1
  29. package/dist/core/sdk.js +24 -101
  30. package/dist/core/sdk.js.map +1 -1
  31. package/dist/core/settings-manager.d.ts +0 -5
  32. package/dist/core/settings-manager.d.ts.map +1 -1
  33. package/dist/core/settings-manager.js +0 -19
  34. package/dist/core/settings-manager.js.map +1 -1
  35. package/dist/core/skills.d.ts.map +1 -1
  36. package/dist/core/skills.js +15 -1
  37. package/dist/core/skills.js.map +1 -1
  38. package/dist/index.d.ts +3 -3
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +4 -8
  41. package/dist/index.js.map +1 -1
  42. package/dist/main.d.ts.map +1 -1
  43. package/dist/main.js +37 -22
  44. package/dist/main.js.map +1 -1
  45. package/dist/modes/interactive/components/footer.d.ts +3 -1
  46. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  47. package/dist/modes/interactive/components/footer.js +4 -3
  48. package/dist/modes/interactive/components/footer.js.map +1 -1
  49. package/dist/modes/interactive/components/model-selector.d.ts +3 -1
  50. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  51. package/dist/modes/interactive/components/model-selector.js +21 -14
  52. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  53. package/dist/modes/interactive/components/oauth-selector.d.ts +3 -1
  54. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  55. package/dist/modes/interactive/components/oauth-selector.js +6 -6
  56. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  57. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  58. package/dist/modes/interactive/interactive-mode.js +53 -48
  59. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  60. package/docs/sdk.md +86 -61
  61. package/examples/custom-tools/hello/index.ts +15 -15
  62. package/examples/custom-tools/question/index.ts +3 -3
  63. package/examples/custom-tools/subagent/agents.ts +1 -2
  64. package/examples/custom-tools/subagent/index.ts +332 -125
  65. package/examples/custom-tools/todo/index.ts +30 -12
  66. package/examples/hooks/confirm-destructive.ts +5 -7
  67. package/examples/hooks/custom-compaction.ts +7 -7
  68. package/examples/hooks/dirty-repo-guard.ts +5 -9
  69. package/examples/hooks/permission-gate.ts +1 -5
  70. package/examples/sdk/02-custom-model.ts +20 -7
  71. package/examples/sdk/04-skills.ts +1 -1
  72. package/examples/sdk/05-tools.ts +11 -14
  73. package/examples/sdk/06-hooks.ts +1 -1
  74. package/examples/sdk/07-context-files.ts +1 -1
  75. package/examples/sdk/08-slash-commands.ts +3 -3
  76. package/examples/sdk/09-api-keys-and-oauth.ts +36 -26
  77. package/examples/sdk/10-settings.ts +2 -2
  78. package/examples/sdk/12-full-control.ts +19 -20
  79. package/examples/sdk/README.md +26 -13
  80. package/package.json +4 -5
  81. package/dist/core/model-config.d.ts +0 -58
  82. package/dist/core/model-config.d.ts.map +0 -1
  83. package/dist/core/model-config.js +0 -384
  84. package/dist/core/model-config.js.map +0 -1
  85. package/dist/core/oauth/index.d.ts +0 -41
  86. package/dist/core/oauth/index.d.ts.map +0 -1
  87. package/dist/core/oauth/index.js +0 -84
  88. package/dist/core/oauth/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/core/skills.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAqB5D,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;CACzB;AAwGD,MAAM,WAAW,wBAAwB;IACxC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,gBAAgB,CAGrF;AA6HD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAuB7D;AAWD,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACxD,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CA+E5E","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { minimatch } from \"minimatch\";\nimport { homedir } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\n\n/**\n * Standard frontmatter fields per Agent Skills spec.\n * See: https://agentskills.io/specification#frontmatter-required\n */\nconst ALLOWED_FRONTMATTER_FIELDS = new Set([\n\t\"name\",\n\t\"description\",\n\t\"license\",\n\t\"compatibility\",\n\t\"metadata\",\n\t\"allowed-tools\",\n]);\n\n/** Max name length per spec */\nconst MAX_NAME_LENGTH = 64;\n\n/** Max description length per spec */\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\nexport interface SkillFrontmatter {\n\tname?: string;\n\tdescription?: string;\n\t[key: string]: unknown;\n}\n\nexport interface Skill {\n\tname: string;\n\tdescription: string;\n\tfilePath: string;\n\tbaseDir: string;\n\tsource: string;\n}\n\nexport interface SkillWarning {\n\tskillPath: string;\n\tmessage: string;\n}\n\nexport interface LoadSkillsResult {\n\tskills: Skill[];\n\twarnings: SkillWarning[];\n}\n\ntype SkillFormat = \"recursive\" | \"claude\";\n\nfunction stripQuotes(value: string): string {\n\tif ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n\t\treturn value.slice(1, -1);\n\t}\n\treturn value;\n}\n\nfunction parseFrontmatter(content: string): { frontmatter: SkillFrontmatter; body: string; allKeys: string[] } {\n\tconst frontmatter: SkillFrontmatter = {};\n\tconst allKeys: string[] = [];\n\n\tconst normalizedContent = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\tif (!normalizedContent.startsWith(\"---\")) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst endIndex = normalizedContent.indexOf(\"\\n---\", 3);\n\tif (endIndex === -1) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst frontmatterBlock = normalizedContent.slice(4, endIndex);\n\tconst body = normalizedContent.slice(endIndex + 4).trim();\n\n\tfor (const line of frontmatterBlock.split(\"\\n\")) {\n\t\tconst match = line.match(/^(\\w[\\w-]*):\\s*(.*)$/);\n\t\tif (match) {\n\t\t\tconst key = match[1];\n\t\t\tconst value = stripQuotes(match[2].trim());\n\t\t\tallKeys.push(key);\n\t\t\tif (key === \"name\") {\n\t\t\t\tfrontmatter.name = value;\n\t\t\t} else if (key === \"description\") {\n\t\t\t\tfrontmatter.description = value;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { frontmatter, body, allKeys };\n}\n\n/**\n * Validate skill name per Agent Skills spec.\n * Returns array of validation error messages (empty if valid).\n */\nfunction validateName(name: string, parentDirName: string): string[] {\n\tconst errors: string[] = [];\n\n\tif (name !== parentDirName) {\n\t\terrors.push(`name \"${name}\" does not match parent directory \"${parentDirName}\"`);\n\t}\n\n\tif (name.length > MAX_NAME_LENGTH) {\n\t\terrors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n\t}\n\n\tif (!/^[a-z0-9-]+$/.test(name)) {\n\t\terrors.push(`name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)`);\n\t}\n\n\tif (name.startsWith(\"-\") || name.endsWith(\"-\")) {\n\t\terrors.push(`name must not start or end with a hyphen`);\n\t}\n\n\tif (name.includes(\"--\")) {\n\t\terrors.push(`name must not contain consecutive hyphens`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validate description per Agent Skills spec.\n */\nfunction validateDescription(description: string | undefined): string[] {\n\tconst errors: string[] = [];\n\n\tif (!description || description.trim() === \"\") {\n\t\terrors.push(`description is required`);\n\t} else if (description.length > MAX_DESCRIPTION_LENGTH) {\n\t\terrors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Check for unknown frontmatter fields.\n */\nfunction validateFrontmatterFields(keys: string[]): string[] {\n\tconst errors: string[] = [];\n\tfor (const key of keys) {\n\t\tif (!ALLOWED_FRONTMATTER_FIELDS.has(key)) {\n\t\t\terrors.push(`unknown frontmatter field \"${key}\"`);\n\t\t}\n\t}\n\treturn errors;\n}\n\nexport interface LoadSkillsFromDirOptions {\n\t/** Directory to scan for skills */\n\tdir: string;\n\t/** Source identifier for these skills */\n\tsource: string;\n}\n\n/**\n * Load skills from a directory recursively.\n * Skills are directories containing a SKILL.md file with frontmatter including a description.\n */\nexport function loadSkillsFromDir(options: LoadSkillsFromDirOptions): LoadSkillsResult {\n\tconst { dir, source } = options;\n\treturn loadSkillsFromDirInternal(dir, source, \"recursive\");\n}\n\nfunction loadSkillsFromDirInternal(dir: string, source: string, format: SkillFormat): LoadSkillsResult {\n\tconst skills: Skill[] = [];\n\tconst warnings: SkillWarning[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn { skills, warnings };\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.name.startsWith(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip node_modules to avoid scanning dependencies\n\t\t\tif (entry.name === \"node_modules\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a directory and follow them\n\t\t\tlet isDirectory = entry.isDirectory();\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisDirectory = stats.isDirectory();\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (format === \"recursive\") {\n\t\t\t\t// Recursive format: scan directories, look for SKILL.md files\n\t\t\t\tif (isDirectory) {\n\t\t\t\t\tconst subResult = loadSkillsFromDirInternal(fullPath, source, format);\n\t\t\t\t\tskills.push(...subResult.skills);\n\t\t\t\t\twarnings.push(...subResult.warnings);\n\t\t\t\t} else if (isFile && entry.name === \"SKILL.md\") {\n\t\t\t\t\tconst result = loadSkillFromFile(fullPath, source);\n\t\t\t\t\tif (result.skill) {\n\t\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t\t}\n\t\t\t\t\twarnings.push(...result.warnings);\n\t\t\t\t}\n\t\t\t} else if (format === \"claude\") {\n\t\t\t\t// Claude format: only one level deep, each directory must contain SKILL.md\n\t\t\t\tif (!isDirectory) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst skillFile = join(fullPath, \"SKILL.md\");\n\t\t\t\tif (!existsSync(skillFile)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst result = loadSkillFromFile(skillFile, source);\n\t\t\t\tif (result.skill) {\n\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t}\n\t\t\t\twarnings.push(...result.warnings);\n\t\t\t}\n\t\t}\n\t} catch {}\n\n\treturn { skills, warnings };\n}\n\nfunction loadSkillFromFile(filePath: string, source: string): { skill: Skill | null; warnings: SkillWarning[] } {\n\tconst warnings: SkillWarning[] = [];\n\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, allKeys } = parseFrontmatter(rawContent);\n\t\tconst skillDir = dirname(filePath);\n\t\tconst parentDirName = basename(skillDir);\n\n\t\t// Validate frontmatter fields\n\t\tconst fieldErrors = validateFrontmatterFields(allKeys);\n\t\tfor (const error of fieldErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Validate description\n\t\tconst descErrors = validateDescription(frontmatter.description);\n\t\tfor (const error of descErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Use name from frontmatter, or fall back to parent directory name\n\t\tconst name = frontmatter.name || parentDirName;\n\n\t\t// Validate name\n\t\tconst nameErrors = validateName(name, parentDirName);\n\t\tfor (const error of nameErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Still load the skill even with warnings (unless description is completely missing)\n\t\tif (!frontmatter.description || frontmatter.description.trim() === \"\") {\n\t\t\treturn { skill: null, warnings };\n\t\t}\n\n\t\treturn {\n\t\t\tskill: {\n\t\t\t\tname,\n\t\t\t\tdescription: frontmatter.description,\n\t\t\t\tfilePath,\n\t\t\t\tbaseDir: skillDir,\n\t\t\t\tsource,\n\t\t\t},\n\t\t\twarnings,\n\t\t};\n\t} catch {\n\t\treturn { skill: null, warnings };\n\t}\n}\n\n/**\n * Format skills for inclusion in a system prompt.\n * Uses XML format per Agent Skills standard.\n * See: https://agentskills.io/integrate-skills\n */\nexport function formatSkillsForPrompt(skills: Skill[]): string {\n\tif (skills.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst lines = [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t];\n\n\tfor (const skill of skills) {\n\t\tlines.push(\" <skill>\");\n\t\tlines.push(` <name>${escapeXml(skill.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(skill.description)}</description>`);\n\t\tlines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n\t\tlines.push(\" </skill>\");\n\t}\n\n\tlines.push(\"</available_skills>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n\nexport interface LoadSkillsOptions extends SkillsSettings {\n\t/** Working directory for project-local skills. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global skills. Default: ~/.pi/agent */\n\tagentDir?: string;\n}\n\n/**\n * Load skills from all configured locations.\n * Returns skills and any validation warnings.\n */\nexport function loadSkills(options: LoadSkillsOptions = {}): LoadSkillsResult {\n\tconst {\n\t\tcwd = process.cwd(),\n\t\tagentDir,\n\t\tenableCodexUser = true,\n\t\tenableClaudeUser = true,\n\t\tenableClaudeProject = true,\n\t\tenablePiUser = true,\n\t\tenablePiProject = true,\n\t\tcustomDirectories = [],\n\t\tignoredSkills = [],\n\t\tincludeSkills = [],\n\t} = options;\n\n\t// Resolve agentDir - if not provided, use default from config\n\tconst resolvedAgentDir = agentDir ?? getAgentDir();\n\n\tconst skillMap = new Map<string, Skill>();\n\tconst allWarnings: SkillWarning[] = [];\n\tconst collisionWarnings: SkillWarning[] = [];\n\n\t// Check if skill name matches any of the include patterns\n\tfunction matchesIncludePatterns(name: string): boolean {\n\t\tif (includeSkills.length === 0) return true; // No filter = include all\n\t\treturn includeSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\t// Check if skill name matches any of the ignore patterns\n\tfunction matchesIgnorePatterns(name: string): boolean {\n\t\tif (ignoredSkills.length === 0) return false;\n\t\treturn ignoredSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\tfunction addSkills(result: LoadSkillsResult) {\n\t\tallWarnings.push(...result.warnings);\n\t\tfor (const skill of result.skills) {\n\t\t\t// Apply ignore filter (glob patterns) - takes precedence over include\n\t\t\tif (matchesIgnorePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Apply include filter (glob patterns)\n\t\t\tif (!matchesIncludePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = skillMap.get(skill.name);\n\t\t\tif (existing) {\n\t\t\t\tcollisionWarnings.push({\n\t\t\t\t\tskillPath: skill.filePath,\n\t\t\t\t\tmessage: `name collision: \"${skill.name}\" already loaded from ${existing.filePath}, skipping this one`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tskillMap.set(skill.name, skill);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (enableCodexUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".codex\", \"skills\"), \"codex-user\", \"recursive\"));\n\t}\n\tif (enableClaudeUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".claude\", \"skills\"), \"claude-user\", \"claude\"));\n\t}\n\tif (enableClaudeProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, \".claude\", \"skills\"), \"claude-project\", \"claude\"));\n\t}\n\tif (enablePiUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, \"skills\"), \"user\", \"recursive\"));\n\t}\n\tif (enablePiProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, CONFIG_DIR_NAME, \"skills\"), \"project\", \"recursive\"));\n\t}\n\tfor (const customDir of customDirectories) {\n\t\taddSkills(loadSkillsFromDirInternal(customDir.replace(/^~(?=$|[\\\\/])/, homedir()), \"custom\", \"recursive\"));\n\t}\n\n\treturn {\n\t\tskills: Array.from(skillMap.values()),\n\t\twarnings: [...allWarnings, ...collisionWarnings],\n\t};\n}\n"]}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/core/skills.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAqB5D,MAAM,WAAW,gBAAgB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;CACzB;AAwGD,MAAM,WAAW,wBAAwB;IACxC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,gBAAgB,CAGrF;AA6HD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAuB7D;AAWD,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACxD,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CA+F5E","sourcesContent":["import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from \"fs\";\nimport { minimatch } from \"minimatch\";\nimport { homedir } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\n\n/**\n * Standard frontmatter fields per Agent Skills spec.\n * See: https://agentskills.io/specification#frontmatter-required\n */\nconst ALLOWED_FRONTMATTER_FIELDS = new Set([\n\t\"name\",\n\t\"description\",\n\t\"license\",\n\t\"compatibility\",\n\t\"metadata\",\n\t\"allowed-tools\",\n]);\n\n/** Max name length per spec */\nconst MAX_NAME_LENGTH = 64;\n\n/** Max description length per spec */\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\nexport interface SkillFrontmatter {\n\tname?: string;\n\tdescription?: string;\n\t[key: string]: unknown;\n}\n\nexport interface Skill {\n\tname: string;\n\tdescription: string;\n\tfilePath: string;\n\tbaseDir: string;\n\tsource: string;\n}\n\nexport interface SkillWarning {\n\tskillPath: string;\n\tmessage: string;\n}\n\nexport interface LoadSkillsResult {\n\tskills: Skill[];\n\twarnings: SkillWarning[];\n}\n\ntype SkillFormat = \"recursive\" | \"claude\";\n\nfunction stripQuotes(value: string): string {\n\tif ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n\t\treturn value.slice(1, -1);\n\t}\n\treturn value;\n}\n\nfunction parseFrontmatter(content: string): { frontmatter: SkillFrontmatter; body: string; allKeys: string[] } {\n\tconst frontmatter: SkillFrontmatter = {};\n\tconst allKeys: string[] = [];\n\n\tconst normalizedContent = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\tif (!normalizedContent.startsWith(\"---\")) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst endIndex = normalizedContent.indexOf(\"\\n---\", 3);\n\tif (endIndex === -1) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst frontmatterBlock = normalizedContent.slice(4, endIndex);\n\tconst body = normalizedContent.slice(endIndex + 4).trim();\n\n\tfor (const line of frontmatterBlock.split(\"\\n\")) {\n\t\tconst match = line.match(/^(\\w[\\w-]*):\\s*(.*)$/);\n\t\tif (match) {\n\t\t\tconst key = match[1];\n\t\t\tconst value = stripQuotes(match[2].trim());\n\t\t\tallKeys.push(key);\n\t\t\tif (key === \"name\") {\n\t\t\t\tfrontmatter.name = value;\n\t\t\t} else if (key === \"description\") {\n\t\t\t\tfrontmatter.description = value;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { frontmatter, body, allKeys };\n}\n\n/**\n * Validate skill name per Agent Skills spec.\n * Returns array of validation error messages (empty if valid).\n */\nfunction validateName(name: string, parentDirName: string): string[] {\n\tconst errors: string[] = [];\n\n\tif (name !== parentDirName) {\n\t\terrors.push(`name \"${name}\" does not match parent directory \"${parentDirName}\"`);\n\t}\n\n\tif (name.length > MAX_NAME_LENGTH) {\n\t\terrors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n\t}\n\n\tif (!/^[a-z0-9-]+$/.test(name)) {\n\t\terrors.push(`name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)`);\n\t}\n\n\tif (name.startsWith(\"-\") || name.endsWith(\"-\")) {\n\t\terrors.push(`name must not start or end with a hyphen`);\n\t}\n\n\tif (name.includes(\"--\")) {\n\t\terrors.push(`name must not contain consecutive hyphens`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validate description per Agent Skills spec.\n */\nfunction validateDescription(description: string | undefined): string[] {\n\tconst errors: string[] = [];\n\n\tif (!description || description.trim() === \"\") {\n\t\terrors.push(`description is required`);\n\t} else if (description.length > MAX_DESCRIPTION_LENGTH) {\n\t\terrors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Check for unknown frontmatter fields.\n */\nfunction validateFrontmatterFields(keys: string[]): string[] {\n\tconst errors: string[] = [];\n\tfor (const key of keys) {\n\t\tif (!ALLOWED_FRONTMATTER_FIELDS.has(key)) {\n\t\t\terrors.push(`unknown frontmatter field \"${key}\"`);\n\t\t}\n\t}\n\treturn errors;\n}\n\nexport interface LoadSkillsFromDirOptions {\n\t/** Directory to scan for skills */\n\tdir: string;\n\t/** Source identifier for these skills */\n\tsource: string;\n}\n\n/**\n * Load skills from a directory recursively.\n * Skills are directories containing a SKILL.md file with frontmatter including a description.\n */\nexport function loadSkillsFromDir(options: LoadSkillsFromDirOptions): LoadSkillsResult {\n\tconst { dir, source } = options;\n\treturn loadSkillsFromDirInternal(dir, source, \"recursive\");\n}\n\nfunction loadSkillsFromDirInternal(dir: string, source: string, format: SkillFormat): LoadSkillsResult {\n\tconst skills: Skill[] = [];\n\tconst warnings: SkillWarning[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn { skills, warnings };\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.name.startsWith(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip node_modules to avoid scanning dependencies\n\t\t\tif (entry.name === \"node_modules\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a directory and follow them\n\t\t\tlet isDirectory = entry.isDirectory();\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisDirectory = stats.isDirectory();\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (format === \"recursive\") {\n\t\t\t\t// Recursive format: scan directories, look for SKILL.md files\n\t\t\t\tif (isDirectory) {\n\t\t\t\t\tconst subResult = loadSkillsFromDirInternal(fullPath, source, format);\n\t\t\t\t\tskills.push(...subResult.skills);\n\t\t\t\t\twarnings.push(...subResult.warnings);\n\t\t\t\t} else if (isFile && entry.name === \"SKILL.md\") {\n\t\t\t\t\tconst result = loadSkillFromFile(fullPath, source);\n\t\t\t\t\tif (result.skill) {\n\t\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t\t}\n\t\t\t\t\twarnings.push(...result.warnings);\n\t\t\t\t}\n\t\t\t} else if (format === \"claude\") {\n\t\t\t\t// Claude format: only one level deep, each directory must contain SKILL.md\n\t\t\t\tif (!isDirectory) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst skillFile = join(fullPath, \"SKILL.md\");\n\t\t\t\tif (!existsSync(skillFile)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst result = loadSkillFromFile(skillFile, source);\n\t\t\t\tif (result.skill) {\n\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t}\n\t\t\t\twarnings.push(...result.warnings);\n\t\t\t}\n\t\t}\n\t} catch {}\n\n\treturn { skills, warnings };\n}\n\nfunction loadSkillFromFile(filePath: string, source: string): { skill: Skill | null; warnings: SkillWarning[] } {\n\tconst warnings: SkillWarning[] = [];\n\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, allKeys } = parseFrontmatter(rawContent);\n\t\tconst skillDir = dirname(filePath);\n\t\tconst parentDirName = basename(skillDir);\n\n\t\t// Validate frontmatter fields\n\t\tconst fieldErrors = validateFrontmatterFields(allKeys);\n\t\tfor (const error of fieldErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Validate description\n\t\tconst descErrors = validateDescription(frontmatter.description);\n\t\tfor (const error of descErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Use name from frontmatter, or fall back to parent directory name\n\t\tconst name = frontmatter.name || parentDirName;\n\n\t\t// Validate name\n\t\tconst nameErrors = validateName(name, parentDirName);\n\t\tfor (const error of nameErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Still load the skill even with warnings (unless description is completely missing)\n\t\tif (!frontmatter.description || frontmatter.description.trim() === \"\") {\n\t\t\treturn { skill: null, warnings };\n\t\t}\n\n\t\treturn {\n\t\t\tskill: {\n\t\t\t\tname,\n\t\t\t\tdescription: frontmatter.description,\n\t\t\t\tfilePath,\n\t\t\t\tbaseDir: skillDir,\n\t\t\t\tsource,\n\t\t\t},\n\t\t\twarnings,\n\t\t};\n\t} catch {\n\t\treturn { skill: null, warnings };\n\t}\n}\n\n/**\n * Format skills for inclusion in a system prompt.\n * Uses XML format per Agent Skills standard.\n * See: https://agentskills.io/integrate-skills\n */\nexport function formatSkillsForPrompt(skills: Skill[]): string {\n\tif (skills.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst lines = [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t];\n\n\tfor (const skill of skills) {\n\t\tlines.push(\" <skill>\");\n\t\tlines.push(` <name>${escapeXml(skill.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(skill.description)}</description>`);\n\t\tlines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n\t\tlines.push(\" </skill>\");\n\t}\n\n\tlines.push(\"</available_skills>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n\nexport interface LoadSkillsOptions extends SkillsSettings {\n\t/** Working directory for project-local skills. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global skills. Default: ~/.pi/agent */\n\tagentDir?: string;\n}\n\n/**\n * Load skills from all configured locations.\n * Returns skills and any validation warnings.\n */\nexport function loadSkills(options: LoadSkillsOptions = {}): LoadSkillsResult {\n\tconst {\n\t\tcwd = process.cwd(),\n\t\tagentDir,\n\t\tenableCodexUser = true,\n\t\tenableClaudeUser = true,\n\t\tenableClaudeProject = true,\n\t\tenablePiUser = true,\n\t\tenablePiProject = true,\n\t\tcustomDirectories = [],\n\t\tignoredSkills = [],\n\t\tincludeSkills = [],\n\t} = options;\n\n\t// Resolve agentDir - if not provided, use default from config\n\tconst resolvedAgentDir = agentDir ?? getAgentDir();\n\n\tconst skillMap = new Map<string, Skill>();\n\tconst realPathSet = new Set<string>();\n\tconst allWarnings: SkillWarning[] = [];\n\tconst collisionWarnings: SkillWarning[] = [];\n\n\t// Check if skill name matches any of the include patterns\n\tfunction matchesIncludePatterns(name: string): boolean {\n\t\tif (includeSkills.length === 0) return true; // No filter = include all\n\t\treturn includeSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\t// Check if skill name matches any of the ignore patterns\n\tfunction matchesIgnorePatterns(name: string): boolean {\n\t\tif (ignoredSkills.length === 0) return false;\n\t\treturn ignoredSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\tfunction addSkills(result: LoadSkillsResult) {\n\t\tallWarnings.push(...result.warnings);\n\t\tfor (const skill of result.skills) {\n\t\t\t// Apply ignore filter (glob patterns) - takes precedence over include\n\t\t\tif (matchesIgnorePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Apply include filter (glob patterns)\n\t\t\tif (!matchesIncludePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Resolve symlinks to detect duplicate files\n\t\t\tlet realPath: string;\n\t\t\ttry {\n\t\t\t\trealPath = realpathSync(skill.filePath);\n\t\t\t} catch {\n\t\t\t\trealPath = skill.filePath;\n\t\t\t}\n\n\t\t\t// Skip silently if we've already loaded this exact file (via symlink)\n\t\t\tif (realPathSet.has(realPath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst existing = skillMap.get(skill.name);\n\t\t\tif (existing) {\n\t\t\t\tcollisionWarnings.push({\n\t\t\t\t\tskillPath: skill.filePath,\n\t\t\t\t\tmessage: `name collision: \"${skill.name}\" already loaded from ${existing.filePath}, skipping this one`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tskillMap.set(skill.name, skill);\n\t\t\t\trealPathSet.add(realPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (enableCodexUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".codex\", \"skills\"), \"codex-user\", \"recursive\"));\n\t}\n\tif (enableClaudeUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".claude\", \"skills\"), \"claude-user\", \"claude\"));\n\t}\n\tif (enableClaudeProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, \".claude\", \"skills\"), \"claude-project\", \"claude\"));\n\t}\n\tif (enablePiUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, \"skills\"), \"user\", \"recursive\"));\n\t}\n\tif (enablePiProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, CONFIG_DIR_NAME, \"skills\"), \"project\", \"recursive\"));\n\t}\n\tfor (const customDir of customDirectories) {\n\t\taddSkills(loadSkillsFromDirInternal(customDir.replace(/^~(?=$|[\\\\/])/, homedir()), \"custom\", \"recursive\"));\n\t}\n\n\treturn {\n\t\tskills: Array.from(skillMap.values()),\n\t\twarnings: [...allWarnings, ...collisionWarnings],\n\t};\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { existsSync, readdirSync, readFileSync, statSync } from "fs";
1
+ import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "fs";
2
2
  import { minimatch } from "minimatch";
3
3
  import { homedir } from "os";
4
4
  import { basename, dirname, join, resolve } from "path";
@@ -261,6 +261,7 @@ export function loadSkills(options = {}) {
261
261
  // Resolve agentDir - if not provided, use default from config
262
262
  const resolvedAgentDir = agentDir ?? getAgentDir();
263
263
  const skillMap = new Map();
264
+ const realPathSet = new Set();
264
265
  const allWarnings = [];
265
266
  const collisionWarnings = [];
266
267
  // Check if skill name matches any of the include patterns
@@ -286,6 +287,18 @@ export function loadSkills(options = {}) {
286
287
  if (!matchesIncludePatterns(skill.name)) {
287
288
  continue;
288
289
  }
290
+ // Resolve symlinks to detect duplicate files
291
+ let realPath;
292
+ try {
293
+ realPath = realpathSync(skill.filePath);
294
+ }
295
+ catch {
296
+ realPath = skill.filePath;
297
+ }
298
+ // Skip silently if we've already loaded this exact file (via symlink)
299
+ if (realPathSet.has(realPath)) {
300
+ continue;
301
+ }
289
302
  const existing = skillMap.get(skill.name);
290
303
  if (existing) {
291
304
  collisionWarnings.push({
@@ -295,6 +308,7 @@ export function loadSkills(options = {}) {
295
308
  }
296
309
  else {
297
310
  skillMap.set(skill.name, skill);
311
+ realPathSet.add(realPath);
298
312
  }
299
313
  }
300
314
  }
@@ -1 +1 @@
1
- {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/core/skills.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { minimatch } from \"minimatch\";\nimport { homedir } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\n\n/**\n * Standard frontmatter fields per Agent Skills spec.\n * See: https://agentskills.io/specification#frontmatter-required\n */\nconst ALLOWED_FRONTMATTER_FIELDS = new Set([\n\t\"name\",\n\t\"description\",\n\t\"license\",\n\t\"compatibility\",\n\t\"metadata\",\n\t\"allowed-tools\",\n]);\n\n/** Max name length per spec */\nconst MAX_NAME_LENGTH = 64;\n\n/** Max description length per spec */\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\nexport interface SkillFrontmatter {\n\tname?: string;\n\tdescription?: string;\n\t[key: string]: unknown;\n}\n\nexport interface Skill {\n\tname: string;\n\tdescription: string;\n\tfilePath: string;\n\tbaseDir: string;\n\tsource: string;\n}\n\nexport interface SkillWarning {\n\tskillPath: string;\n\tmessage: string;\n}\n\nexport interface LoadSkillsResult {\n\tskills: Skill[];\n\twarnings: SkillWarning[];\n}\n\ntype SkillFormat = \"recursive\" | \"claude\";\n\nfunction stripQuotes(value: string): string {\n\tif ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n\t\treturn value.slice(1, -1);\n\t}\n\treturn value;\n}\n\nfunction parseFrontmatter(content: string): { frontmatter: SkillFrontmatter; body: string; allKeys: string[] } {\n\tconst frontmatter: SkillFrontmatter = {};\n\tconst allKeys: string[] = [];\n\n\tconst normalizedContent = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\tif (!normalizedContent.startsWith(\"---\")) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst endIndex = normalizedContent.indexOf(\"\\n---\", 3);\n\tif (endIndex === -1) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst frontmatterBlock = normalizedContent.slice(4, endIndex);\n\tconst body = normalizedContent.slice(endIndex + 4).trim();\n\n\tfor (const line of frontmatterBlock.split(\"\\n\")) {\n\t\tconst match = line.match(/^(\\w[\\w-]*):\\s*(.*)$/);\n\t\tif (match) {\n\t\t\tconst key = match[1];\n\t\t\tconst value = stripQuotes(match[2].trim());\n\t\t\tallKeys.push(key);\n\t\t\tif (key === \"name\") {\n\t\t\t\tfrontmatter.name = value;\n\t\t\t} else if (key === \"description\") {\n\t\t\t\tfrontmatter.description = value;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { frontmatter, body, allKeys };\n}\n\n/**\n * Validate skill name per Agent Skills spec.\n * Returns array of validation error messages (empty if valid).\n */\nfunction validateName(name: string, parentDirName: string): string[] {\n\tconst errors: string[] = [];\n\n\tif (name !== parentDirName) {\n\t\terrors.push(`name \"${name}\" does not match parent directory \"${parentDirName}\"`);\n\t}\n\n\tif (name.length > MAX_NAME_LENGTH) {\n\t\terrors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n\t}\n\n\tif (!/^[a-z0-9-]+$/.test(name)) {\n\t\terrors.push(`name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)`);\n\t}\n\n\tif (name.startsWith(\"-\") || name.endsWith(\"-\")) {\n\t\terrors.push(`name must not start or end with a hyphen`);\n\t}\n\n\tif (name.includes(\"--\")) {\n\t\terrors.push(`name must not contain consecutive hyphens`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validate description per Agent Skills spec.\n */\nfunction validateDescription(description: string | undefined): string[] {\n\tconst errors: string[] = [];\n\n\tif (!description || description.trim() === \"\") {\n\t\terrors.push(`description is required`);\n\t} else if (description.length > MAX_DESCRIPTION_LENGTH) {\n\t\terrors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Check for unknown frontmatter fields.\n */\nfunction validateFrontmatterFields(keys: string[]): string[] {\n\tconst errors: string[] = [];\n\tfor (const key of keys) {\n\t\tif (!ALLOWED_FRONTMATTER_FIELDS.has(key)) {\n\t\t\terrors.push(`unknown frontmatter field \"${key}\"`);\n\t\t}\n\t}\n\treturn errors;\n}\n\nexport interface LoadSkillsFromDirOptions {\n\t/** Directory to scan for skills */\n\tdir: string;\n\t/** Source identifier for these skills */\n\tsource: string;\n}\n\n/**\n * Load skills from a directory recursively.\n * Skills are directories containing a SKILL.md file with frontmatter including a description.\n */\nexport function loadSkillsFromDir(options: LoadSkillsFromDirOptions): LoadSkillsResult {\n\tconst { dir, source } = options;\n\treturn loadSkillsFromDirInternal(dir, source, \"recursive\");\n}\n\nfunction loadSkillsFromDirInternal(dir: string, source: string, format: SkillFormat): LoadSkillsResult {\n\tconst skills: Skill[] = [];\n\tconst warnings: SkillWarning[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn { skills, warnings };\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.name.startsWith(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip node_modules to avoid scanning dependencies\n\t\t\tif (entry.name === \"node_modules\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a directory and follow them\n\t\t\tlet isDirectory = entry.isDirectory();\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisDirectory = stats.isDirectory();\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (format === \"recursive\") {\n\t\t\t\t// Recursive format: scan directories, look for SKILL.md files\n\t\t\t\tif (isDirectory) {\n\t\t\t\t\tconst subResult = loadSkillsFromDirInternal(fullPath, source, format);\n\t\t\t\t\tskills.push(...subResult.skills);\n\t\t\t\t\twarnings.push(...subResult.warnings);\n\t\t\t\t} else if (isFile && entry.name === \"SKILL.md\") {\n\t\t\t\t\tconst result = loadSkillFromFile(fullPath, source);\n\t\t\t\t\tif (result.skill) {\n\t\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t\t}\n\t\t\t\t\twarnings.push(...result.warnings);\n\t\t\t\t}\n\t\t\t} else if (format === \"claude\") {\n\t\t\t\t// Claude format: only one level deep, each directory must contain SKILL.md\n\t\t\t\tif (!isDirectory) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst skillFile = join(fullPath, \"SKILL.md\");\n\t\t\t\tif (!existsSync(skillFile)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst result = loadSkillFromFile(skillFile, source);\n\t\t\t\tif (result.skill) {\n\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t}\n\t\t\t\twarnings.push(...result.warnings);\n\t\t\t}\n\t\t}\n\t} catch {}\n\n\treturn { skills, warnings };\n}\n\nfunction loadSkillFromFile(filePath: string, source: string): { skill: Skill | null; warnings: SkillWarning[] } {\n\tconst warnings: SkillWarning[] = [];\n\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, allKeys } = parseFrontmatter(rawContent);\n\t\tconst skillDir = dirname(filePath);\n\t\tconst parentDirName = basename(skillDir);\n\n\t\t// Validate frontmatter fields\n\t\tconst fieldErrors = validateFrontmatterFields(allKeys);\n\t\tfor (const error of fieldErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Validate description\n\t\tconst descErrors = validateDescription(frontmatter.description);\n\t\tfor (const error of descErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Use name from frontmatter, or fall back to parent directory name\n\t\tconst name = frontmatter.name || parentDirName;\n\n\t\t// Validate name\n\t\tconst nameErrors = validateName(name, parentDirName);\n\t\tfor (const error of nameErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Still load the skill even with warnings (unless description is completely missing)\n\t\tif (!frontmatter.description || frontmatter.description.trim() === \"\") {\n\t\t\treturn { skill: null, warnings };\n\t\t}\n\n\t\treturn {\n\t\t\tskill: {\n\t\t\t\tname,\n\t\t\t\tdescription: frontmatter.description,\n\t\t\t\tfilePath,\n\t\t\t\tbaseDir: skillDir,\n\t\t\t\tsource,\n\t\t\t},\n\t\t\twarnings,\n\t\t};\n\t} catch {\n\t\treturn { skill: null, warnings };\n\t}\n}\n\n/**\n * Format skills for inclusion in a system prompt.\n * Uses XML format per Agent Skills standard.\n * See: https://agentskills.io/integrate-skills\n */\nexport function formatSkillsForPrompt(skills: Skill[]): string {\n\tif (skills.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst lines = [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t];\n\n\tfor (const skill of skills) {\n\t\tlines.push(\" <skill>\");\n\t\tlines.push(` <name>${escapeXml(skill.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(skill.description)}</description>`);\n\t\tlines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n\t\tlines.push(\" </skill>\");\n\t}\n\n\tlines.push(\"</available_skills>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n\nexport interface LoadSkillsOptions extends SkillsSettings {\n\t/** Working directory for project-local skills. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global skills. Default: ~/.pi/agent */\n\tagentDir?: string;\n}\n\n/**\n * Load skills from all configured locations.\n * Returns skills and any validation warnings.\n */\nexport function loadSkills(options: LoadSkillsOptions = {}): LoadSkillsResult {\n\tconst {\n\t\tcwd = process.cwd(),\n\t\tagentDir,\n\t\tenableCodexUser = true,\n\t\tenableClaudeUser = true,\n\t\tenableClaudeProject = true,\n\t\tenablePiUser = true,\n\t\tenablePiProject = true,\n\t\tcustomDirectories = [],\n\t\tignoredSkills = [],\n\t\tincludeSkills = [],\n\t} = options;\n\n\t// Resolve agentDir - if not provided, use default from config\n\tconst resolvedAgentDir = agentDir ?? getAgentDir();\n\n\tconst skillMap = new Map<string, Skill>();\n\tconst allWarnings: SkillWarning[] = [];\n\tconst collisionWarnings: SkillWarning[] = [];\n\n\t// Check if skill name matches any of the include patterns\n\tfunction matchesIncludePatterns(name: string): boolean {\n\t\tif (includeSkills.length === 0) return true; // No filter = include all\n\t\treturn includeSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\t// Check if skill name matches any of the ignore patterns\n\tfunction matchesIgnorePatterns(name: string): boolean {\n\t\tif (ignoredSkills.length === 0) return false;\n\t\treturn ignoredSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\tfunction addSkills(result: LoadSkillsResult) {\n\t\tallWarnings.push(...result.warnings);\n\t\tfor (const skill of result.skills) {\n\t\t\t// Apply ignore filter (glob patterns) - takes precedence over include\n\t\t\tif (matchesIgnorePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Apply include filter (glob patterns)\n\t\t\tif (!matchesIncludePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = skillMap.get(skill.name);\n\t\t\tif (existing) {\n\t\t\t\tcollisionWarnings.push({\n\t\t\t\t\tskillPath: skill.filePath,\n\t\t\t\t\tmessage: `name collision: \"${skill.name}\" already loaded from ${existing.filePath}, skipping this one`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tskillMap.set(skill.name, skill);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (enableCodexUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".codex\", \"skills\"), \"codex-user\", \"recursive\"));\n\t}\n\tif (enableClaudeUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".claude\", \"skills\"), \"claude-user\", \"claude\"));\n\t}\n\tif (enableClaudeProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, \".claude\", \"skills\"), \"claude-project\", \"claude\"));\n\t}\n\tif (enablePiUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, \"skills\"), \"user\", \"recursive\"));\n\t}\n\tif (enablePiProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, CONFIG_DIR_NAME, \"skills\"), \"project\", \"recursive\"));\n\t}\n\tfor (const customDir of customDirectories) {\n\t\taddSkills(loadSkillsFromDirInternal(customDir.replace(/^~(?=$|[\\\\/])/, homedir()), \"custom\", \"recursive\"));\n\t}\n\n\treturn {\n\t\tskills: Array.from(skillMap.values()),\n\t\twarnings: [...allWarnings, ...collisionWarnings],\n\t};\n}\n"]}
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/core/skills.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from \"fs\";\nimport { minimatch } from \"minimatch\";\nimport { homedir } from \"os\";\nimport { basename, dirname, join, resolve } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\n\n/**\n * Standard frontmatter fields per Agent Skills spec.\n * See: https://agentskills.io/specification#frontmatter-required\n */\nconst ALLOWED_FRONTMATTER_FIELDS = new Set([\n\t\"name\",\n\t\"description\",\n\t\"license\",\n\t\"compatibility\",\n\t\"metadata\",\n\t\"allowed-tools\",\n]);\n\n/** Max name length per spec */\nconst MAX_NAME_LENGTH = 64;\n\n/** Max description length per spec */\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\nexport interface SkillFrontmatter {\n\tname?: string;\n\tdescription?: string;\n\t[key: string]: unknown;\n}\n\nexport interface Skill {\n\tname: string;\n\tdescription: string;\n\tfilePath: string;\n\tbaseDir: string;\n\tsource: string;\n}\n\nexport interface SkillWarning {\n\tskillPath: string;\n\tmessage: string;\n}\n\nexport interface LoadSkillsResult {\n\tskills: Skill[];\n\twarnings: SkillWarning[];\n}\n\ntype SkillFormat = \"recursive\" | \"claude\";\n\nfunction stripQuotes(value: string): string {\n\tif ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n\t\treturn value.slice(1, -1);\n\t}\n\treturn value;\n}\n\nfunction parseFrontmatter(content: string): { frontmatter: SkillFrontmatter; body: string; allKeys: string[] } {\n\tconst frontmatter: SkillFrontmatter = {};\n\tconst allKeys: string[] = [];\n\n\tconst normalizedContent = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\tif (!normalizedContent.startsWith(\"---\")) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst endIndex = normalizedContent.indexOf(\"\\n---\", 3);\n\tif (endIndex === -1) {\n\t\treturn { frontmatter, body: normalizedContent, allKeys };\n\t}\n\n\tconst frontmatterBlock = normalizedContent.slice(4, endIndex);\n\tconst body = normalizedContent.slice(endIndex + 4).trim();\n\n\tfor (const line of frontmatterBlock.split(\"\\n\")) {\n\t\tconst match = line.match(/^(\\w[\\w-]*):\\s*(.*)$/);\n\t\tif (match) {\n\t\t\tconst key = match[1];\n\t\t\tconst value = stripQuotes(match[2].trim());\n\t\t\tallKeys.push(key);\n\t\t\tif (key === \"name\") {\n\t\t\t\tfrontmatter.name = value;\n\t\t\t} else if (key === \"description\") {\n\t\t\t\tfrontmatter.description = value;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { frontmatter, body, allKeys };\n}\n\n/**\n * Validate skill name per Agent Skills spec.\n * Returns array of validation error messages (empty if valid).\n */\nfunction validateName(name: string, parentDirName: string): string[] {\n\tconst errors: string[] = [];\n\n\tif (name !== parentDirName) {\n\t\terrors.push(`name \"${name}\" does not match parent directory \"${parentDirName}\"`);\n\t}\n\n\tif (name.length > MAX_NAME_LENGTH) {\n\t\terrors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n\t}\n\n\tif (!/^[a-z0-9-]+$/.test(name)) {\n\t\terrors.push(`name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)`);\n\t}\n\n\tif (name.startsWith(\"-\") || name.endsWith(\"-\")) {\n\t\terrors.push(`name must not start or end with a hyphen`);\n\t}\n\n\tif (name.includes(\"--\")) {\n\t\terrors.push(`name must not contain consecutive hyphens`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validate description per Agent Skills spec.\n */\nfunction validateDescription(description: string | undefined): string[] {\n\tconst errors: string[] = [];\n\n\tif (!description || description.trim() === \"\") {\n\t\terrors.push(`description is required`);\n\t} else if (description.length > MAX_DESCRIPTION_LENGTH) {\n\t\terrors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Check for unknown frontmatter fields.\n */\nfunction validateFrontmatterFields(keys: string[]): string[] {\n\tconst errors: string[] = [];\n\tfor (const key of keys) {\n\t\tif (!ALLOWED_FRONTMATTER_FIELDS.has(key)) {\n\t\t\terrors.push(`unknown frontmatter field \"${key}\"`);\n\t\t}\n\t}\n\treturn errors;\n}\n\nexport interface LoadSkillsFromDirOptions {\n\t/** Directory to scan for skills */\n\tdir: string;\n\t/** Source identifier for these skills */\n\tsource: string;\n}\n\n/**\n * Load skills from a directory recursively.\n * Skills are directories containing a SKILL.md file with frontmatter including a description.\n */\nexport function loadSkillsFromDir(options: LoadSkillsFromDirOptions): LoadSkillsResult {\n\tconst { dir, source } = options;\n\treturn loadSkillsFromDirInternal(dir, source, \"recursive\");\n}\n\nfunction loadSkillsFromDirInternal(dir: string, source: string, format: SkillFormat): LoadSkillsResult {\n\tconst skills: Skill[] = [];\n\tconst warnings: SkillWarning[] = [];\n\n\tif (!existsSync(dir)) {\n\t\treturn { skills, warnings };\n\t}\n\n\ttry {\n\t\tconst entries = readdirSync(dir, { withFileTypes: true });\n\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.name.startsWith(\".\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip node_modules to avoid scanning dependencies\n\t\t\tif (entry.name === \"node_modules\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fullPath = join(dir, entry.name);\n\n\t\t\t// For symlinks, check if they point to a directory and follow them\n\t\t\tlet isDirectory = entry.isDirectory();\n\t\t\tlet isFile = entry.isFile();\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst stats = statSync(fullPath);\n\t\t\t\t\tisDirectory = stats.isDirectory();\n\t\t\t\t\tisFile = stats.isFile();\n\t\t\t\t} catch {\n\t\t\t\t\t// Broken symlink, skip it\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (format === \"recursive\") {\n\t\t\t\t// Recursive format: scan directories, look for SKILL.md files\n\t\t\t\tif (isDirectory) {\n\t\t\t\t\tconst subResult = loadSkillsFromDirInternal(fullPath, source, format);\n\t\t\t\t\tskills.push(...subResult.skills);\n\t\t\t\t\twarnings.push(...subResult.warnings);\n\t\t\t\t} else if (isFile && entry.name === \"SKILL.md\") {\n\t\t\t\t\tconst result = loadSkillFromFile(fullPath, source);\n\t\t\t\t\tif (result.skill) {\n\t\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t\t}\n\t\t\t\t\twarnings.push(...result.warnings);\n\t\t\t\t}\n\t\t\t} else if (format === \"claude\") {\n\t\t\t\t// Claude format: only one level deep, each directory must contain SKILL.md\n\t\t\t\tif (!isDirectory) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst skillFile = join(fullPath, \"SKILL.md\");\n\t\t\t\tif (!existsSync(skillFile)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst result = loadSkillFromFile(skillFile, source);\n\t\t\t\tif (result.skill) {\n\t\t\t\t\tskills.push(result.skill);\n\t\t\t\t}\n\t\t\t\twarnings.push(...result.warnings);\n\t\t\t}\n\t\t}\n\t} catch {}\n\n\treturn { skills, warnings };\n}\n\nfunction loadSkillFromFile(filePath: string, source: string): { skill: Skill | null; warnings: SkillWarning[] } {\n\tconst warnings: SkillWarning[] = [];\n\n\ttry {\n\t\tconst rawContent = readFileSync(filePath, \"utf-8\");\n\t\tconst { frontmatter, allKeys } = parseFrontmatter(rawContent);\n\t\tconst skillDir = dirname(filePath);\n\t\tconst parentDirName = basename(skillDir);\n\n\t\t// Validate frontmatter fields\n\t\tconst fieldErrors = validateFrontmatterFields(allKeys);\n\t\tfor (const error of fieldErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Validate description\n\t\tconst descErrors = validateDescription(frontmatter.description);\n\t\tfor (const error of descErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Use name from frontmatter, or fall back to parent directory name\n\t\tconst name = frontmatter.name || parentDirName;\n\n\t\t// Validate name\n\t\tconst nameErrors = validateName(name, parentDirName);\n\t\tfor (const error of nameErrors) {\n\t\t\twarnings.push({ skillPath: filePath, message: error });\n\t\t}\n\n\t\t// Still load the skill even with warnings (unless description is completely missing)\n\t\tif (!frontmatter.description || frontmatter.description.trim() === \"\") {\n\t\t\treturn { skill: null, warnings };\n\t\t}\n\n\t\treturn {\n\t\t\tskill: {\n\t\t\t\tname,\n\t\t\t\tdescription: frontmatter.description,\n\t\t\t\tfilePath,\n\t\t\t\tbaseDir: skillDir,\n\t\t\t\tsource,\n\t\t\t},\n\t\t\twarnings,\n\t\t};\n\t} catch {\n\t\treturn { skill: null, warnings };\n\t}\n}\n\n/**\n * Format skills for inclusion in a system prompt.\n * Uses XML format per Agent Skills standard.\n * See: https://agentskills.io/integrate-skills\n */\nexport function formatSkillsForPrompt(skills: Skill[]): string {\n\tif (skills.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst lines = [\n\t\t\"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n\t\t\"Use the read tool to load a skill's file when the task matches its description.\",\n\t\t\"\",\n\t\t\"<available_skills>\",\n\t];\n\n\tfor (const skill of skills) {\n\t\tlines.push(\" <skill>\");\n\t\tlines.push(` <name>${escapeXml(skill.name)}</name>`);\n\t\tlines.push(` <description>${escapeXml(skill.description)}</description>`);\n\t\tlines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n\t\tlines.push(\" </skill>\");\n\t}\n\n\tlines.push(\"</available_skills>\");\n\n\treturn lines.join(\"\\n\");\n}\n\nfunction escapeXml(str: string): string {\n\treturn str\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n\nexport interface LoadSkillsOptions extends SkillsSettings {\n\t/** Working directory for project-local skills. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global skills. Default: ~/.pi/agent */\n\tagentDir?: string;\n}\n\n/**\n * Load skills from all configured locations.\n * Returns skills and any validation warnings.\n */\nexport function loadSkills(options: LoadSkillsOptions = {}): LoadSkillsResult {\n\tconst {\n\t\tcwd = process.cwd(),\n\t\tagentDir,\n\t\tenableCodexUser = true,\n\t\tenableClaudeUser = true,\n\t\tenableClaudeProject = true,\n\t\tenablePiUser = true,\n\t\tenablePiProject = true,\n\t\tcustomDirectories = [],\n\t\tignoredSkills = [],\n\t\tincludeSkills = [],\n\t} = options;\n\n\t// Resolve agentDir - if not provided, use default from config\n\tconst resolvedAgentDir = agentDir ?? getAgentDir();\n\n\tconst skillMap = new Map<string, Skill>();\n\tconst realPathSet = new Set<string>();\n\tconst allWarnings: SkillWarning[] = [];\n\tconst collisionWarnings: SkillWarning[] = [];\n\n\t// Check if skill name matches any of the include patterns\n\tfunction matchesIncludePatterns(name: string): boolean {\n\t\tif (includeSkills.length === 0) return true; // No filter = include all\n\t\treturn includeSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\t// Check if skill name matches any of the ignore patterns\n\tfunction matchesIgnorePatterns(name: string): boolean {\n\t\tif (ignoredSkills.length === 0) return false;\n\t\treturn ignoredSkills.some((pattern) => minimatch(name, pattern));\n\t}\n\n\tfunction addSkills(result: LoadSkillsResult) {\n\t\tallWarnings.push(...result.warnings);\n\t\tfor (const skill of result.skills) {\n\t\t\t// Apply ignore filter (glob patterns) - takes precedence over include\n\t\t\tif (matchesIgnorePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Apply include filter (glob patterns)\n\t\t\tif (!matchesIncludePatterns(skill.name)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Resolve symlinks to detect duplicate files\n\t\t\tlet realPath: string;\n\t\t\ttry {\n\t\t\t\trealPath = realpathSync(skill.filePath);\n\t\t\t} catch {\n\t\t\t\trealPath = skill.filePath;\n\t\t\t}\n\n\t\t\t// Skip silently if we've already loaded this exact file (via symlink)\n\t\t\tif (realPathSet.has(realPath)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst existing = skillMap.get(skill.name);\n\t\t\tif (existing) {\n\t\t\t\tcollisionWarnings.push({\n\t\t\t\t\tskillPath: skill.filePath,\n\t\t\t\t\tmessage: `name collision: \"${skill.name}\" already loaded from ${existing.filePath}, skipping this one`,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tskillMap.set(skill.name, skill);\n\t\t\t\trealPathSet.add(realPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (enableCodexUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".codex\", \"skills\"), \"codex-user\", \"recursive\"));\n\t}\n\tif (enableClaudeUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(homedir(), \".claude\", \"skills\"), \"claude-user\", \"claude\"));\n\t}\n\tif (enableClaudeProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, \".claude\", \"skills\"), \"claude-project\", \"claude\"));\n\t}\n\tif (enablePiUser) {\n\t\taddSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, \"skills\"), \"user\", \"recursive\"));\n\t}\n\tif (enablePiProject) {\n\t\taddSkills(loadSkillsFromDirInternal(resolve(cwd, CONFIG_DIR_NAME, \"skills\"), \"project\", \"recursive\"));\n\t}\n\tfor (const customDir of customDirectories) {\n\t\taddSkills(loadSkillsFromDirInternal(customDir.replace(/^~(?=$|[\\\\/])/, homedir()), \"custom\", \"recursive\"));\n\t}\n\n\treturn {\n\t\tskills: Array.from(skillMap.values()),\n\t\twarnings: [...allWarnings, ...collisionWarnings],\n\t};\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  export { AgentSession, type AgentSessionConfig, type AgentSessionEvent, type AgentSessionEventListener, type CompactionResult, type ModelCycleResult, type PromptOptions, type SessionStats, } from "./core/agent-session.js";
2
+ export { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from "./core/auth-storage.js";
2
3
  export { type CutPointResult, calculateContextTokens, compact, DEFAULT_COMPACTION_SETTINGS, estimateTokens, findCutPoint, findTurnStartIndex, generateSummary, getLastAssistantUsage, shouldCompact, } from "./core/compaction.js";
3
4
  export type { AgentToolUpdateCallback, CustomAgentTool, CustomToolFactory, CustomToolsLoadResult, ExecResult, LoadedCustomTool, RenderResultOptions, SessionEvent as ToolSessionEvent, ToolAPI, ToolUIContext, } from "./core/custom-tools/index.js";
4
5
  export { discoverAndLoadCustomTools, loadCustomTools } from "./core/custom-tools/index.js";
5
6
  export type { AgentEndEvent, AgentStartEvent, BashToolResultEvent, CustomToolResultEvent, EditToolResultEvent, FindToolResultEvent, GrepToolResultEvent, HookAPI, HookEvent, HookEventContext, HookFactory, HookUIContext, LsToolResultEvent, ReadToolResultEvent, SessionEvent, SessionEventResult, ToolCallEvent, ToolCallEventResult, ToolResultEvent, ToolResultEventResult, TurnEndEvent, TurnStartEvent, WriteToolResultEvent, } from "./core/hooks/index.js";
6
7
  export { isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isReadToolResult, isWriteToolResult, } from "./core/hooks/index.js";
7
8
  export { messageTransformer } from "./core/messages.js";
8
- export { findModel, getApiKeyForModel, getAvailableModels } from "./core/model-config.js";
9
- export { getOAuthProviders, login, logout, type OAuthAuthInfo, type OAuthPrompt, type OAuthProvider, } from "./core/oauth/index.js";
10
- export { type BuildSystemPromptOptions, buildSystemPrompt, type CreateAgentSessionOptions, type CreateAgentSessionResult, configureOAuthStorage, createAgentSession, createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, defaultGetApiKey, discoverAvailableModels, discoverContextFiles, discoverCustomTools, discoverHooks, discoverModels, discoverSkills, discoverSlashCommands, type FileSlashCommand, findModel as findModelByProviderAndId, loadSettings, readOnlyTools, } from "./core/sdk.js";
9
+ export { ModelRegistry } from "./core/model-registry.js";
10
+ export { type BuildSystemPromptOptions, buildSystemPrompt, type CreateAgentSessionOptions, type CreateAgentSessionResult, createAgentSession, createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, discoverAuthStorage, discoverContextFiles, discoverCustomTools, discoverHooks, discoverModels, discoverSkills, discoverSlashCommands, type FileSlashCommand, loadSettings, readOnlyTools, } from "./core/sdk.js";
11
11
  export { buildSessionContext, type CompactionEntry, createSummaryMessage, getLatestCompactionEntry, type ModelChangeEntry, parseSessionEntries, type SessionContext as LoadedSession, type SessionEntry, type SessionHeader, type SessionInfo, SessionManager, type SessionMessageEntry, SUMMARY_PREFIX, SUMMARY_SUFFIX, type ThinkingLevelChangeEntry, } from "./core/session-manager.js";
12
12
  export { type CompactionSettings, type RetrySettings, type Settings, SettingsManager, type SkillsSettings, } from "./core/settings-manager.js";
13
13
  export { formatSkillsForPrompt, type LoadSkillsFromDirOptions, type LoadSkillsResult, loadSkills, loadSkillsFromDir, type Skill, type SkillFrontmatter, type SkillWarning, } from "./core/skills.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core session management\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype CompactionResult,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Compaction\nexport {\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tshouldCompact,\n} from \"./core/compaction.js\";\n// Custom tools\nexport type {\n\tAgentToolUpdateCallback,\n\tCustomAgentTool,\n\tCustomToolFactory,\n\tCustomToolsLoadResult,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n\tSessionEvent as ToolSessionEvent,\n\tToolAPI,\n\tToolUIContext,\n} from \"./core/custom-tools/index.js\";\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./core/custom-tools/index.js\";\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tBashToolResultEvent,\n\tCustomToolResultEvent,\n\tEditToolResultEvent,\n\tFindToolResultEvent,\n\tGrepToolResultEvent,\n\tHookAPI,\n\tHookEvent,\n\tHookEventContext,\n\tHookFactory,\n\tHookUIContext,\n\tLsToolResultEvent,\n\tReadToolResultEvent,\n\tSessionEvent,\n\tSessionEventResult,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tWriteToolResultEvent,\n} from \"./core/hooks/index.js\";\n// Hook system types and type guards\nexport {\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n} from \"./core/hooks/index.js\";\nexport { messageTransformer } from \"./core/messages.js\";\n// Model configuration and OAuth\nexport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nexport {\n\tgetOAuthProviders,\n\tlogin,\n\tlogout,\n\ttype OAuthAuthInfo,\n\ttype OAuthPrompt,\n\ttype OAuthProvider,\n} from \"./core/oauth/index.js\";\n// SDK for programmatic usage\nexport {\n\ttype BuildSystemPromptOptions,\n\tbuildSystemPrompt,\n\ttype CreateAgentSessionOptions,\n\ttype CreateAgentSessionResult,\n\t// Configuration\n\tconfigureOAuthStorage,\n\t// Factory\n\tcreateAgentSession,\n\tcreateBashTool,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\t// Helpers\n\tdefaultGetApiKey,\n\tdiscoverAvailableModels,\n\tdiscoverContextFiles,\n\tdiscoverCustomTools,\n\tdiscoverHooks,\n\t// Discovery\n\tdiscoverModels,\n\tdiscoverSkills,\n\tdiscoverSlashCommands,\n\ttype FileSlashCommand,\n\tfindModel as findModelByProviderAndId,\n\tloadSettings,\n\t// Pre-built tools (use process.cwd())\n\treadOnlyTools,\n} from \"./core/sdk.js\";\nexport {\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tcreateSummaryMessage,\n\tgetLatestCompactionEntry,\n\ttype ModelChangeEntry,\n\tparseSessionEntries,\n\ttype SessionContext as LoadedSession,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\ttype SessionInfo,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\tSUMMARY_PREFIX,\n\tSUMMARY_SUFFIX,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype RetrySettings,\n\ttype Settings,\n\tSettingsManager,\n\ttype SkillsSettings,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n\ttype SkillWarning,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashToolDetails,\n\tbashTool,\n\tcodingTools,\n\teditTool,\n\ttype FindToolDetails,\n\tfindTool,\n\ttype GrepToolDetails,\n\tgrepTool,\n\ttype LsToolDetails,\n\tlsTool,\n\ttype ReadToolDetails,\n\treadTool,\n\ttype TruncationResult,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Theme utilities for custom tools\nexport { getMarkdownTheme } from \"./modes/interactive/theme/theme.js\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core session management\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype CompactionResult,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Auth and model registry\nexport { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from \"./core/auth-storage.js\";\n// Compaction\nexport {\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tshouldCompact,\n} from \"./core/compaction.js\";\n// Custom tools\nexport type {\n\tAgentToolUpdateCallback,\n\tCustomAgentTool,\n\tCustomToolFactory,\n\tCustomToolsLoadResult,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n\tSessionEvent as ToolSessionEvent,\n\tToolAPI,\n\tToolUIContext,\n} from \"./core/custom-tools/index.js\";\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./core/custom-tools/index.js\";\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tBashToolResultEvent,\n\tCustomToolResultEvent,\n\tEditToolResultEvent,\n\tFindToolResultEvent,\n\tGrepToolResultEvent,\n\tHookAPI,\n\tHookEvent,\n\tHookEventContext,\n\tHookFactory,\n\tHookUIContext,\n\tLsToolResultEvent,\n\tReadToolResultEvent,\n\tSessionEvent,\n\tSessionEventResult,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tWriteToolResultEvent,\n} from \"./core/hooks/index.js\";\n// Hook system types and type guards\nexport {\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n} from \"./core/hooks/index.js\";\nexport { messageTransformer } from \"./core/messages.js\";\nexport { ModelRegistry } from \"./core/model-registry.js\";\n// SDK for programmatic usage\nexport {\n\ttype BuildSystemPromptOptions,\n\tbuildSystemPrompt,\n\ttype CreateAgentSessionOptions,\n\ttype CreateAgentSessionResult,\n\t// Factory\n\tcreateAgentSession,\n\tcreateBashTool,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\t// Discovery\n\tdiscoverAuthStorage,\n\tdiscoverContextFiles,\n\tdiscoverCustomTools,\n\tdiscoverHooks,\n\tdiscoverModels,\n\tdiscoverSkills,\n\tdiscoverSlashCommands,\n\ttype FileSlashCommand,\n\tloadSettings,\n\t// Pre-built tools (use process.cwd())\n\treadOnlyTools,\n} from \"./core/sdk.js\";\nexport {\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tcreateSummaryMessage,\n\tgetLatestCompactionEntry,\n\ttype ModelChangeEntry,\n\tparseSessionEntries,\n\ttype SessionContext as LoadedSession,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\ttype SessionInfo,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\tSUMMARY_PREFIX,\n\tSUMMARY_SUFFIX,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype RetrySettings,\n\ttype Settings,\n\tSettingsManager,\n\ttype SkillsSettings,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n\ttype SkillWarning,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashToolDetails,\n\tbashTool,\n\tcodingTools,\n\teditTool,\n\ttype FindToolDetails,\n\tfindTool,\n\ttype GrepToolDetails,\n\tgrepTool,\n\ttype LsToolDetails,\n\tlsTool,\n\ttype ReadToolDetails,\n\treadTool,\n\ttype TruncationResult,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Theme utilities for custom tools\nexport { getMarkdownTheme } from \"./modes/interactive/theme/theme.js\";\n"]}
package/dist/index.js CHANGED
@@ -1,26 +1,22 @@
1
1
  // Core session management
2
2
  export { AgentSession, } from "./core/agent-session.js";
3
+ // Auth and model registry
4
+ export { AuthStorage } from "./core/auth-storage.js";
3
5
  // Compaction
4
6
  export { calculateContextTokens, compact, DEFAULT_COMPACTION_SETTINGS, estimateTokens, findCutPoint, findTurnStartIndex, generateSummary, getLastAssistantUsage, shouldCompact, } from "./core/compaction.js";
5
7
  export { discoverAndLoadCustomTools, loadCustomTools } from "./core/custom-tools/index.js";
6
8
  // Hook system types and type guards
7
9
  export { isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isReadToolResult, isWriteToolResult, } from "./core/hooks/index.js";
8
10
  export { messageTransformer } from "./core/messages.js";
9
- // Model configuration and OAuth
10
- export { findModel, getApiKeyForModel, getAvailableModels } from "./core/model-config.js";
11
- export { getOAuthProviders, login, logout, } from "./core/oauth/index.js";
11
+ export { ModelRegistry } from "./core/model-registry.js";
12
12
  // SDK for programmatic usage
13
13
  export { buildSystemPrompt,
14
- // Configuration
15
- configureOAuthStorage,
16
14
  // Factory
17
15
  createAgentSession, createBashTool,
18
16
  // Tool factories (for custom cwd)
19
17
  createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool,
20
- // Helpers
21
- defaultGetApiKey, discoverAvailableModels, discoverContextFiles, discoverCustomTools, discoverHooks,
22
18
  // Discovery
23
- discoverModels, discoverSkills, discoverSlashCommands, findModel as findModelByProviderAndId, loadSettings,
19
+ discoverAuthStorage, discoverContextFiles, discoverCustomTools, discoverHooks, discoverModels, discoverSkills, discoverSlashCommands, loadSettings,
24
20
  // Pre-built tools (use process.cwd())
25
21
  readOnlyTools, } from "./core/sdk.js";
26
22
  export { buildSessionContext, createSummaryMessage, getLatestCompactionEntry, parseSessionEntries, SessionManager, SUMMARY_PREFIX, SUMMARY_SUFFIX, } from "./core/session-manager.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EACN,YAAY,GAQZ,MAAM,yBAAyB,CAAC;AACjC,aAAa;AACb,OAAO,EAEN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,aAAa,GACb,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA0B3F,oCAAoC;AACpC,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,gCAAgC;AAChC,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACN,iBAAiB,EACjB,KAAK,EACL,MAAM,GAIN,MAAM,uBAAuB,CAAC;AAC/B,6BAA6B;AAC7B,OAAO,EAEN,iBAAiB;AAGjB,gBAAgB;AAChB,qBAAqB;AACrB,UAAU;AACV,kBAAkB,EAClB,cAAc;AACd,kCAAkC;AAClC,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,eAAe;AACf,UAAU;AACV,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa;AACb,YAAY;AACZ,cAAc,EACd,cAAc,EACd,qBAAqB,EAErB,SAAS,IAAI,wBAAwB,EACrC,YAAY;AACZ,sCAAsC;AACtC,aAAa,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,mBAAmB,EAEnB,oBAAoB,EACpB,wBAAwB,EAExB,mBAAmB,EAKnB,cAAc,EAEd,cAAc,EACd,cAAc,GAEd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAIN,eAAe,GAEf,MAAM,4BAA4B,CAAC;AACpC,SAAS;AACT,OAAO,EACN,qBAAqB,EAGrB,UAAU,EACV,iBAAiB,GAIjB,MAAM,kBAAkB,CAAC;AAC1B,QAAQ;AACR,OAAO,EAEN,QAAQ,EACR,WAAW,EACX,QAAQ,EAER,QAAQ,EAER,QAAQ,EAER,MAAM,EAEN,QAAQ,EAER,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,mBAAmB;AACnB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,mCAAmC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC","sourcesContent":["// Core session management\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype CompactionResult,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Compaction\nexport {\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tshouldCompact,\n} from \"./core/compaction.js\";\n// Custom tools\nexport type {\n\tAgentToolUpdateCallback,\n\tCustomAgentTool,\n\tCustomToolFactory,\n\tCustomToolsLoadResult,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n\tSessionEvent as ToolSessionEvent,\n\tToolAPI,\n\tToolUIContext,\n} from \"./core/custom-tools/index.js\";\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./core/custom-tools/index.js\";\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tBashToolResultEvent,\n\tCustomToolResultEvent,\n\tEditToolResultEvent,\n\tFindToolResultEvent,\n\tGrepToolResultEvent,\n\tHookAPI,\n\tHookEvent,\n\tHookEventContext,\n\tHookFactory,\n\tHookUIContext,\n\tLsToolResultEvent,\n\tReadToolResultEvent,\n\tSessionEvent,\n\tSessionEventResult,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tWriteToolResultEvent,\n} from \"./core/hooks/index.js\";\n// Hook system types and type guards\nexport {\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n} from \"./core/hooks/index.js\";\nexport { messageTransformer } from \"./core/messages.js\";\n// Model configuration and OAuth\nexport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nexport {\n\tgetOAuthProviders,\n\tlogin,\n\tlogout,\n\ttype OAuthAuthInfo,\n\ttype OAuthPrompt,\n\ttype OAuthProvider,\n} from \"./core/oauth/index.js\";\n// SDK for programmatic usage\nexport {\n\ttype BuildSystemPromptOptions,\n\tbuildSystemPrompt,\n\ttype CreateAgentSessionOptions,\n\ttype CreateAgentSessionResult,\n\t// Configuration\n\tconfigureOAuthStorage,\n\t// Factory\n\tcreateAgentSession,\n\tcreateBashTool,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\t// Helpers\n\tdefaultGetApiKey,\n\tdiscoverAvailableModels,\n\tdiscoverContextFiles,\n\tdiscoverCustomTools,\n\tdiscoverHooks,\n\t// Discovery\n\tdiscoverModels,\n\tdiscoverSkills,\n\tdiscoverSlashCommands,\n\ttype FileSlashCommand,\n\tfindModel as findModelByProviderAndId,\n\tloadSettings,\n\t// Pre-built tools (use process.cwd())\n\treadOnlyTools,\n} from \"./core/sdk.js\";\nexport {\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tcreateSummaryMessage,\n\tgetLatestCompactionEntry,\n\ttype ModelChangeEntry,\n\tparseSessionEntries,\n\ttype SessionContext as LoadedSession,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\ttype SessionInfo,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\tSUMMARY_PREFIX,\n\tSUMMARY_SUFFIX,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype RetrySettings,\n\ttype Settings,\n\tSettingsManager,\n\ttype SkillsSettings,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n\ttype SkillWarning,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashToolDetails,\n\tbashTool,\n\tcodingTools,\n\teditTool,\n\ttype FindToolDetails,\n\tfindTool,\n\ttype GrepToolDetails,\n\tgrepTool,\n\ttype LsToolDetails,\n\tlsTool,\n\ttype ReadToolDetails,\n\treadTool,\n\ttype TruncationResult,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Theme utilities for custom tools\nexport { getMarkdownTheme } from \"./modes/interactive/theme/theme.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EACN,YAAY,GAQZ,MAAM,yBAAyB,CAAC;AACjC,0BAA0B;AAC1B,OAAO,EAA8C,WAAW,EAAwB,MAAM,wBAAwB,CAAC;AACvH,aAAa;AACb,OAAO,EAEN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,aAAa,GACb,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA0B3F,oCAAoC;AACpC,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,6BAA6B;AAC7B,OAAO,EAEN,iBAAiB;AAGjB,UAAU;AACV,kBAAkB,EAClB,cAAc;AACd,kCAAkC;AAClC,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,eAAe;AACf,YAAY;AACZ,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,cAAc,EACd,qBAAqB,EAErB,YAAY;AACZ,sCAAsC;AACtC,aAAa,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,mBAAmB,EAEnB,oBAAoB,EACpB,wBAAwB,EAExB,mBAAmB,EAKnB,cAAc,EAEd,cAAc,EACd,cAAc,GAEd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAIN,eAAe,GAEf,MAAM,4BAA4B,CAAC;AACpC,SAAS;AACT,OAAO,EACN,qBAAqB,EAGrB,UAAU,EACV,iBAAiB,GAIjB,MAAM,kBAAkB,CAAC;AAC1B,QAAQ;AACR,OAAO,EAEN,QAAQ,EACR,WAAW,EACX,QAAQ,EAER,QAAQ,EAER,QAAQ,EAER,MAAM,EAEN,QAAQ,EAER,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,mBAAmB;AACnB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,mCAAmC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC","sourcesContent":["// Core session management\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype CompactionResult,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Auth and model registry\nexport { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from \"./core/auth-storage.js\";\n// Compaction\nexport {\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tshouldCompact,\n} from \"./core/compaction.js\";\n// Custom tools\nexport type {\n\tAgentToolUpdateCallback,\n\tCustomAgentTool,\n\tCustomToolFactory,\n\tCustomToolsLoadResult,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n\tSessionEvent as ToolSessionEvent,\n\tToolAPI,\n\tToolUIContext,\n} from \"./core/custom-tools/index.js\";\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./core/custom-tools/index.js\";\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tBashToolResultEvent,\n\tCustomToolResultEvent,\n\tEditToolResultEvent,\n\tFindToolResultEvent,\n\tGrepToolResultEvent,\n\tHookAPI,\n\tHookEvent,\n\tHookEventContext,\n\tHookFactory,\n\tHookUIContext,\n\tLsToolResultEvent,\n\tReadToolResultEvent,\n\tSessionEvent,\n\tSessionEventResult,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tWriteToolResultEvent,\n} from \"./core/hooks/index.js\";\n// Hook system types and type guards\nexport {\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n} from \"./core/hooks/index.js\";\nexport { messageTransformer } from \"./core/messages.js\";\nexport { ModelRegistry } from \"./core/model-registry.js\";\n// SDK for programmatic usage\nexport {\n\ttype BuildSystemPromptOptions,\n\tbuildSystemPrompt,\n\ttype CreateAgentSessionOptions,\n\ttype CreateAgentSessionResult,\n\t// Factory\n\tcreateAgentSession,\n\tcreateBashTool,\n\t// Tool factories (for custom cwd)\n\tcreateCodingTools,\n\tcreateEditTool,\n\tcreateFindTool,\n\tcreateGrepTool,\n\tcreateLsTool,\n\tcreateReadOnlyTools,\n\tcreateReadTool,\n\tcreateWriteTool,\n\t// Discovery\n\tdiscoverAuthStorage,\n\tdiscoverContextFiles,\n\tdiscoverCustomTools,\n\tdiscoverHooks,\n\tdiscoverModels,\n\tdiscoverSkills,\n\tdiscoverSlashCommands,\n\ttype FileSlashCommand,\n\tloadSettings,\n\t// Pre-built tools (use process.cwd())\n\treadOnlyTools,\n} from \"./core/sdk.js\";\nexport {\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tcreateSummaryMessage,\n\tgetLatestCompactionEntry,\n\ttype ModelChangeEntry,\n\tparseSessionEntries,\n\ttype SessionContext as LoadedSession,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\ttype SessionInfo,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\tSUMMARY_PREFIX,\n\tSUMMARY_SUFFIX,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype RetrySettings,\n\ttype Settings,\n\tSettingsManager,\n\ttype SkillsSettings,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n\ttype SkillWarning,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashToolDetails,\n\tbashTool,\n\tcodingTools,\n\teditTool,\n\ttype FindToolDetails,\n\tfindTool,\n\ttype GrepToolDetails,\n\tgrepTool,\n\ttype LsToolDetails,\n\tlsTool,\n\ttype ReadToolDetails,\n\treadTool,\n\ttype TruncationResult,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Theme utilities for custom tools\nexport { getMarkdownTheme } from \"./modes/interactive/theme/theme.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuPH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAoJxC","sourcesContent":["/**\n * Main entry point for the coding agent CLI.\n *\n * This file handles CLI argument parsing and translates them into\n * createAgentSession() options. The SDK does the heavy lifting.\n */\n\nimport type { Attachment } from \"@mariozechner/pi-agent-core\";\nimport { supportsXhigh } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\n\nimport { type Args, parseArgs, printHelp } from \"./cli/args.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { selectSession } from \"./cli/session-picker.js\";\nimport { getModelsPath, VERSION } from \"./config.js\";\nimport type { AgentSession } from \"./core/agent-session.js\";\nimport type { LoadedCustomTool } from \"./core/custom-tools/index.js\";\nimport { exportFromFile } from \"./core/export-html.js\";\nimport type { HookUIContext } from \"./core/index.js\";\nimport { findModel } from \"./core/model-config.js\";\nimport { resolveModelScope, type ScopedModel } from \"./core/model-resolver.js\";\nimport { type CreateAgentSessionOptions, configureOAuthStorage, createAgentSession } from \"./core/sdk.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { resolvePromptInput } from \"./core/system-prompt.js\";\nimport { printTimings, time } from \"./core/timings.js\";\nimport { allTools } from \"./core/tools/index.js\";\nimport { InteractiveMode, runPrintMode, runRpcMode } from \"./modes/index.js\";\nimport { initTheme, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { getChangelogPath, getNewEntries, parseChangelog } from \"./utils/changelog.js\";\nimport { ensureTool } from \"./utils/tools-manager.js\";\n\nasync function checkForNewVersion(currentVersion: string): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(\"https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest\");\n\t\tif (!response.ok) return null;\n\n\t\tconst data = (await response.json()) as { version?: string };\n\t\tconst latestVersion = data.version;\n\n\t\tif (latestVersion && latestVersion !== currentVersion) {\n\t\t\treturn latestVersion;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function runInteractiveMode(\n\tsession: AgentSession,\n\tversion: string,\n\tchangelogMarkdown: string | null,\n\tmodelFallbackMessage: string | undefined,\n\tversionCheckPromise: Promise<string | null>,\n\tinitialMessages: string[],\n\tcustomTools: LoadedCustomTool[],\n\tsetToolUIContext: (uiContext: HookUIContext, hasUI: boolean) => void,\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n\tfdPath: string | null = null,\n): Promise<void> {\n\tconst mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);\n\n\tawait mode.init();\n\n\tversionCheckPromise.then((newVersion) => {\n\t\tif (newVersion) {\n\t\t\tmode.showNewVersionNotification(newVersion);\n\t\t}\n\t});\n\n\tmode.renderInitialMessages(session.state);\n\n\tif (modelFallbackMessage) {\n\t\tmode.showWarning(modelFallbackMessage);\n\t}\n\n\tif (initialMessage) {\n\t\ttry {\n\t\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\tfor (const message of initialMessages) {\n\t\ttry {\n\t\t\tawait session.prompt(message);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\twhile (true) {\n\t\tconst userInput = await mode.getUserInput();\n\t\ttry {\n\t\t\tawait session.prompt(userInput);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n}\n\nasync function prepareInitialMessage(parsed: Args): Promise<{\n\tinitialMessage?: string;\n\tinitialAttachments?: Attachment[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn {};\n\t}\n\n\tconst { textContent, imageAttachments } = await processFileArguments(parsed.fileArgs);\n\n\tlet initialMessage: string;\n\tif (parsed.messages.length > 0) {\n\t\tinitialMessage = textContent + parsed.messages[0];\n\t\tparsed.messages.shift();\n\t} else {\n\t\tinitialMessage = textContent;\n\t}\n\n\treturn {\n\t\tinitialMessage,\n\t\tinitialAttachments: imageAttachments.length > 0 ? imageAttachments : undefined,\n\t};\n}\n\nfunction getChangelogForDisplay(parsed: Args, settingsManager: SettingsManager): string | null {\n\tif (parsed.continue || parsed.resume) {\n\t\treturn null;\n\t}\n\n\tconst lastVersion = settingsManager.getLastChangelogVersion();\n\tconst changelogPath = getChangelogPath();\n\tconst entries = parseChangelog(changelogPath);\n\n\tif (!lastVersion) {\n\t\tif (entries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn entries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t} else {\n\t\tconst newEntries = getNewEntries(entries, lastVersion);\n\t\tif (newEntries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn newEntries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction createSessionManager(parsed: Args, cwd: string): SessionManager | null {\n\tif (parsed.noSession) {\n\t\treturn SessionManager.inMemory();\n\t}\n\tif (parsed.session) {\n\t\treturn SessionManager.open(parsed.session);\n\t}\n\tif (parsed.continue) {\n\t\treturn SessionManager.continueRecent(cwd);\n\t}\n\t// --resume is handled separately (needs picker UI)\n\t// Default case (new session) returns null, SDK will create one\n\treturn null;\n}\n\nfunction buildSessionOptions(\n\tparsed: Args,\n\tscopedModels: ScopedModel[],\n\tsessionManager: SessionManager | null,\n): CreateAgentSessionOptions {\n\tconst options: CreateAgentSessionOptions = {};\n\n\tconst resolvedSystemPrompt = resolvePromptInput(parsed.systemPrompt, \"system prompt\");\n\tconst resolvedAppendPrompt = resolvePromptInput(parsed.appendSystemPrompt, \"append system prompt\");\n\n\tif (sessionManager) {\n\t\toptions.sessionManager = sessionManager;\n\t}\n\n\t// Model from CLI\n\tif (parsed.provider && parsed.model) {\n\t\tconst { model, error } = findModel(parsed.provider, parsed.model);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (!model) {\n\t\t\tconsole.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\toptions.model = model;\n\t} else if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\toptions.model = scopedModels[0].model;\n\t}\n\n\t// Thinking level\n\tif (parsed.thinking) {\n\t\toptions.thinkingLevel = parsed.thinking;\n\t} else if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\toptions.thinkingLevel = scopedModels[0].thinkingLevel;\n\t}\n\n\t// Scoped models for Ctrl+P cycling\n\tif (scopedModels.length > 0) {\n\t\toptions.scopedModels = scopedModels;\n\t}\n\n\t// API key from CLI\n\tif (parsed.apiKey) {\n\t\toptions.getApiKey = async () => parsed.apiKey!;\n\t}\n\n\t// System prompt\n\tif (resolvedSystemPrompt && resolvedAppendPrompt) {\n\t\toptions.systemPrompt = `${resolvedSystemPrompt}\\n\\n${resolvedAppendPrompt}`;\n\t} else if (resolvedSystemPrompt) {\n\t\toptions.systemPrompt = resolvedSystemPrompt;\n\t} else if (resolvedAppendPrompt) {\n\t\toptions.systemPrompt = (defaultPrompt) => `${defaultPrompt}\\n\\n${resolvedAppendPrompt}`;\n\t}\n\n\t// Tools\n\tif (parsed.tools) {\n\t\toptions.tools = parsed.tools.map((name) => allTools[name]);\n\t}\n\n\t// Skills\n\tif (parsed.noSkills) {\n\t\toptions.skills = [];\n\t}\n\n\t// Additional hook paths from CLI\n\tif (parsed.hooks && parsed.hooks.length > 0) {\n\t\toptions.additionalHookPaths = parsed.hooks;\n\t}\n\n\t// Additional custom tool paths from CLI\n\tif (parsed.customTools && parsed.customTools.length > 0) {\n\t\toptions.additionalCustomToolPaths = parsed.customTools;\n\t}\n\n\treturn options;\n}\n\nexport async function main(args: string[]) {\n\ttime(\"start\");\n\tconfigureOAuthStorage();\n\ttime(\"configureOAuthStorage\");\n\n\tconst parsed = parseArgs(args);\n\ttime(\"parseArgs\");\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tconst settingsManager = SettingsManager.create(process.cwd());\n\t\tawait listModels(searchPattern, settingsManager);\n\t\treturn;\n\t}\n\n\tif (parsed.export) {\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tconst result = exportFromFile(parsed.export, outputPath);\n\t\t\tconsole.log(`Exported to: ${result}`);\n\t\t\treturn;\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tconst cwd = process.cwd();\n\tconst { initialMessage, initialAttachments } = await prepareInitialMessage(parsed);\n\ttime(\"prepareInitialMessage\");\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\tconst settingsManager = SettingsManager.create(cwd);\n\ttime(\"SettingsManager.create\");\n\tinitTheme(settingsManager.getTheme(), isInteractive);\n\ttime(\"initTheme\");\n\n\tlet scopedModels: ScopedModel[] = [];\n\tif (parsed.models && parsed.models.length > 0) {\n\t\tscopedModels = await resolveModelScope(parsed.models, settingsManager);\n\t\ttime(\"resolveModelScope\");\n\t}\n\n\t// Create session manager based on CLI flags\n\tlet sessionManager = createSessionManager(parsed, cwd);\n\ttime(\"createSessionManager\");\n\n\t// Handle --resume: show session picker\n\tif (parsed.resume) {\n\t\tconst sessions = SessionManager.list(cwd);\n\t\ttime(\"SessionManager.list\");\n\t\tif (sessions.length === 0) {\n\t\t\tconsole.log(chalk.dim(\"No sessions found\"));\n\t\t\treturn;\n\t\t}\n\t\tconst selectedPath = await selectSession(sessions);\n\t\ttime(\"selectSession\");\n\t\tif (!selectedPath) {\n\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\treturn;\n\t\t}\n\t\tsessionManager = SessionManager.open(selectedPath);\n\t}\n\n\tconst sessionOptions = buildSessionOptions(parsed, scopedModels, sessionManager);\n\ttime(\"buildSessionOptions\");\n\tconst { session, customToolsResult, modelFallbackMessage } = await createAgentSession(sessionOptions);\n\ttime(\"createAgentSession\");\n\n\tif (!isInteractive && !session.model) {\n\t\tconsole.error(chalk.red(\"No models available.\"));\n\t\tconsole.error(chalk.yellow(\"\\nSet an API key environment variable:\"));\n\t\tconsole.error(\" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\");\n\t\tconsole.error(chalk.yellow(`\\nOr create ${getModelsPath()}`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Clamp thinking level to model capabilities (for CLI override case)\n\tif (session.model && parsed.thinking) {\n\t\tlet effectiveThinking = parsed.thinking;\n\t\tif (!session.model.reasoning) {\n\t\t\teffectiveThinking = \"off\";\n\t\t} else if (effectiveThinking === \"xhigh\" && !supportsXhigh(session.model)) {\n\t\t\teffectiveThinking = \"high\";\n\t\t}\n\t\tif (effectiveThinking !== session.thinkingLevel) {\n\t\t\tsession.setThinkingLevel(effectiveThinking);\n\t\t}\n\t}\n\n\tif (mode === \"rpc\") {\n\t\tawait runRpcMode(session);\n\t} else if (isInteractive) {\n\t\tconst versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);\n\t\tconst changelogMarkdown = getChangelogForDisplay(parsed, settingsManager);\n\n\t\tif (scopedModels.length > 0) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel !== \"off\" ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\tconst fdPath = await ensureTool(\"fd\");\n\t\ttime(\"ensureTool(fd)\");\n\n\t\tprintTimings();\n\t\tawait runInteractiveMode(\n\t\t\tsession,\n\t\t\tVERSION,\n\t\t\tchangelogMarkdown,\n\t\t\tmodelFallbackMessage,\n\t\t\tversionCheckPromise,\n\t\t\tparsed.messages,\n\t\t\tcustomToolsResult.tools,\n\t\t\tcustomToolsResult.setUIContext,\n\t\t\tinitialMessage,\n\t\t\tinitialAttachments,\n\t\t\tfdPath,\n\t\t);\n\t} else {\n\t\tawait runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);\n\t\tstopThemeWatcher();\n\t\tif (process.stdout.writableLength > 0) {\n\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t}\n\t\tprocess.exit(0);\n\t}\n}\n"]}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6PH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAwKxC","sourcesContent":["/**\n * Main entry point for the coding agent CLI.\n *\n * This file handles CLI argument parsing and translates them into\n * createAgentSession() options. The SDK does the heavy lifting.\n */\n\nimport type { Attachment } from \"@mariozechner/pi-agent-core\";\nimport { supportsXhigh } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { join } from \"path\";\nimport { type Args, parseArgs, printHelp } from \"./cli/args.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { selectSession } from \"./cli/session-picker.js\";\nimport { getAgentDir, getModelsPath, VERSION } from \"./config.js\";\nimport type { AgentSession } from \"./core/agent-session.js\";\nimport { AuthStorage } from \"./core/auth-storage.js\";\nimport type { LoadedCustomTool } from \"./core/custom-tools/index.js\";\nimport { exportFromFile } from \"./core/export-html.js\";\nimport type { HookUIContext } from \"./core/index.js\";\nimport type { ModelRegistry } from \"./core/model-registry.js\";\nimport { resolveModelScope, type ScopedModel } from \"./core/model-resolver.js\";\nimport { type CreateAgentSessionOptions, createAgentSession, discoverAuthStorage, discoverModels } from \"./core/sdk.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { resolvePromptInput } from \"./core/system-prompt.js\";\nimport { printTimings, time } from \"./core/timings.js\";\nimport { allTools } from \"./core/tools/index.js\";\nimport { InteractiveMode, runPrintMode, runRpcMode } from \"./modes/index.js\";\nimport { initTheme, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { getChangelogPath, getNewEntries, parseChangelog } from \"./utils/changelog.js\";\nimport { ensureTool } from \"./utils/tools-manager.js\";\n\nasync function checkForNewVersion(currentVersion: string): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(\"https://registry.npmjs.org/@mariozechner/pi -coding-agent/latest\");\n\t\tif (!response.ok) return null;\n\n\t\tconst data = (await response.json()) as { version?: string };\n\t\tconst latestVersion = data.version;\n\n\t\tif (latestVersion && latestVersion !== currentVersion) {\n\t\t\treturn latestVersion;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function runInteractiveMode(\n\tsession: AgentSession,\n\tversion: string,\n\tchangelogMarkdown: string | null,\n\tmodelFallbackMessage: string | undefined,\n\tmodelsJsonError: string | null,\n\tmigratedProviders: string[],\n\tversionCheckPromise: Promise<string | null>,\n\tinitialMessages: string[],\n\tcustomTools: LoadedCustomTool[],\n\tsetToolUIContext: (uiContext: HookUIContext, hasUI: boolean) => void,\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n\tfdPath: string | null = null,\n): Promise<void> {\n\tconst mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);\n\n\tawait mode.init();\n\n\tversionCheckPromise.then((newVersion) => {\n\t\tif (newVersion) {\n\t\t\tmode.showNewVersionNotification(newVersion);\n\t\t}\n\t});\n\n\tmode.renderInitialMessages(session.state);\n\n\tif (migratedProviders.length > 0) {\n\t\tmode.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(\", \")}`);\n\t}\n\n\tif (modelsJsonError) {\n\t\tmode.showError(`models.json error: ${modelsJsonError}`);\n\t}\n\n\tif (modelFallbackMessage) {\n\t\tmode.showWarning(modelFallbackMessage);\n\t}\n\n\tif (initialMessage) {\n\t\ttry {\n\t\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\tfor (const message of initialMessages) {\n\t\ttry {\n\t\t\tawait session.prompt(message);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\twhile (true) {\n\t\tconst userInput = await mode.getUserInput();\n\t\ttry {\n\t\t\tawait session.prompt(userInput);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n}\n\nasync function prepareInitialMessage(parsed: Args): Promise<{\n\tinitialMessage?: string;\n\tinitialAttachments?: Attachment[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn {};\n\t}\n\n\tconst { textContent, imageAttachments } = await processFileArguments(parsed.fileArgs);\n\n\tlet initialMessage: string;\n\tif (parsed.messages.length > 0) {\n\t\tinitialMessage = textContent + parsed.messages[0];\n\t\tparsed.messages.shift();\n\t} else {\n\t\tinitialMessage = textContent;\n\t}\n\n\treturn {\n\t\tinitialMessage,\n\t\tinitialAttachments: imageAttachments.length > 0 ? imageAttachments : undefined,\n\t};\n}\n\nfunction getChangelogForDisplay(parsed: Args, settingsManager: SettingsManager): string | null {\n\tif (parsed.continue || parsed.resume) {\n\t\treturn null;\n\t}\n\n\tconst lastVersion = settingsManager.getLastChangelogVersion();\n\tconst changelogPath = getChangelogPath();\n\tconst entries = parseChangelog(changelogPath);\n\n\tif (!lastVersion) {\n\t\tif (entries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn entries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t} else {\n\t\tconst newEntries = getNewEntries(entries, lastVersion);\n\t\tif (newEntries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn newEntries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction createSessionManager(parsed: Args, cwd: string): SessionManager | null {\n\tif (parsed.noSession) {\n\t\treturn SessionManager.inMemory();\n\t}\n\tif (parsed.session) {\n\t\treturn SessionManager.open(parsed.session);\n\t}\n\tif (parsed.continue) {\n\t\treturn SessionManager.continueRecent(cwd);\n\t}\n\t// --resume is handled separately (needs picker UI)\n\t// Default case (new session) returns null, SDK will create one\n\treturn null;\n}\n\nfunction buildSessionOptions(\n\tparsed: Args,\n\tscopedModels: ScopedModel[],\n\tsessionManager: SessionManager | null,\n\tmodelRegistry: ModelRegistry,\n): CreateAgentSessionOptions {\n\tconst options: CreateAgentSessionOptions = {};\n\n\tconst resolvedSystemPrompt = resolvePromptInput(parsed.systemPrompt, \"system prompt\");\n\tconst resolvedAppendPrompt = resolvePromptInput(parsed.appendSystemPrompt, \"append system prompt\");\n\n\tif (sessionManager) {\n\t\toptions.sessionManager = sessionManager;\n\t}\n\n\t// Model from CLI\n\tif (parsed.provider && parsed.model) {\n\t\tconst model = modelRegistry.find(parsed.provider, parsed.model);\n\t\tif (!model) {\n\t\t\tconsole.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\toptions.model = model;\n\t} else if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\toptions.model = scopedModels[0].model;\n\t}\n\n\t// Thinking level\n\tif (parsed.thinking) {\n\t\toptions.thinkingLevel = parsed.thinking;\n\t} else if (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\toptions.thinkingLevel = scopedModels[0].thinkingLevel;\n\t}\n\n\t// Scoped models for Ctrl+P cycling\n\tif (scopedModels.length > 0) {\n\t\toptions.scopedModels = scopedModels;\n\t}\n\n\t// API key from CLI - set in authStorage\n\t// (handled by caller before createAgentSession)\n\n\t// System prompt\n\tif (resolvedSystemPrompt && resolvedAppendPrompt) {\n\t\toptions.systemPrompt = `${resolvedSystemPrompt}\\n\\n${resolvedAppendPrompt}`;\n\t} else if (resolvedSystemPrompt) {\n\t\toptions.systemPrompt = resolvedSystemPrompt;\n\t} else if (resolvedAppendPrompt) {\n\t\toptions.systemPrompt = (defaultPrompt) => `${defaultPrompt}\\n\\n${resolvedAppendPrompt}`;\n\t}\n\n\t// Tools\n\tif (parsed.tools) {\n\t\toptions.tools = parsed.tools.map((name) => allTools[name]);\n\t}\n\n\t// Skills\n\tif (parsed.noSkills) {\n\t\toptions.skills = [];\n\t}\n\n\t// Additional hook paths from CLI\n\tif (parsed.hooks && parsed.hooks.length > 0) {\n\t\toptions.additionalHookPaths = parsed.hooks;\n\t}\n\n\t// Additional custom tool paths from CLI\n\tif (parsed.customTools && parsed.customTools.length > 0) {\n\t\toptions.additionalCustomToolPaths = parsed.customTools;\n\t}\n\n\treturn options;\n}\n\nexport async function main(args: string[]) {\n\ttime(\"start\");\n\n\t// Migrate legacy oauth.json and settings.json apiKeys to auth.json\n\tconst agentDir = getAgentDir();\n\tconst migratedProviders = AuthStorage.migrateLegacy(join(agentDir, \"auth.json\"), agentDir);\n\n\t// Create AuthStorage and ModelRegistry upfront\n\tconst authStorage = discoverAuthStorage();\n\tconst modelRegistry = discoverModels(authStorage);\n\ttime(\"discoverModels\");\n\n\tconst parsed = parseArgs(args);\n\ttime(\"parseArgs\");\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(modelRegistry, searchPattern);\n\t\treturn;\n\t}\n\n\tif (parsed.export) {\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tconst result = exportFromFile(parsed.export, outputPath);\n\t\t\tconsole.log(`Exported to: ${result}`);\n\t\t\treturn;\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tconst cwd = process.cwd();\n\tconst { initialMessage, initialAttachments } = await prepareInitialMessage(parsed);\n\ttime(\"prepareInitialMessage\");\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\tconst settingsManager = SettingsManager.create(cwd);\n\ttime(\"SettingsManager.create\");\n\tinitTheme(settingsManager.getTheme(), isInteractive);\n\ttime(\"initTheme\");\n\n\tlet scopedModels: ScopedModel[] = [];\n\tif (parsed.models && parsed.models.length > 0) {\n\t\tscopedModels = await resolveModelScope(parsed.models, modelRegistry);\n\t\ttime(\"resolveModelScope\");\n\t}\n\n\t// Create session manager based on CLI flags\n\tlet sessionManager = createSessionManager(parsed, cwd);\n\ttime(\"createSessionManager\");\n\n\t// Handle --resume: show session picker\n\tif (parsed.resume) {\n\t\tconst sessions = SessionManager.list(cwd);\n\t\ttime(\"SessionManager.list\");\n\t\tif (sessions.length === 0) {\n\t\t\tconsole.log(chalk.dim(\"No sessions found\"));\n\t\t\treturn;\n\t\t}\n\t\tconst selectedPath = await selectSession(sessions);\n\t\ttime(\"selectSession\");\n\t\tif (!selectedPath) {\n\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\treturn;\n\t\t}\n\t\tsessionManager = SessionManager.open(selectedPath);\n\t}\n\n\tconst sessionOptions = buildSessionOptions(parsed, scopedModels, sessionManager, modelRegistry);\n\tsessionOptions.authStorage = authStorage;\n\tsessionOptions.modelRegistry = modelRegistry;\n\n\t// Handle CLI --api-key as runtime override (not persisted)\n\tif (parsed.apiKey) {\n\t\tif (!sessionOptions.model) {\n\t\t\tconsole.error(chalk.red(\"--api-key requires a model to be specified via --provider/--model or -m/--models\"));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tauthStorage.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey);\n\t}\n\n\ttime(\"buildSessionOptions\");\n\tconst { session, customToolsResult, modelFallbackMessage } = await createAgentSession(sessionOptions);\n\ttime(\"createAgentSession\");\n\n\tif (!isInteractive && !session.model) {\n\t\tconsole.error(chalk.red(\"No models available.\"));\n\t\tconsole.error(chalk.yellow(\"\\nSet an API key environment variable:\"));\n\t\tconsole.error(\" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\");\n\t\tconsole.error(chalk.yellow(`\\nOr create ${getModelsPath()}`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Clamp thinking level to model capabilities (for CLI override case)\n\tif (session.model && parsed.thinking) {\n\t\tlet effectiveThinking = parsed.thinking;\n\t\tif (!session.model.reasoning) {\n\t\t\teffectiveThinking = \"off\";\n\t\t} else if (effectiveThinking === \"xhigh\" && !supportsXhigh(session.model)) {\n\t\t\teffectiveThinking = \"high\";\n\t\t}\n\t\tif (effectiveThinking !== session.thinkingLevel) {\n\t\t\tsession.setThinkingLevel(effectiveThinking);\n\t\t}\n\t}\n\n\tif (mode === \"rpc\") {\n\t\tawait runRpcMode(session);\n\t} else if (isInteractive) {\n\t\tconst versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);\n\t\tconst changelogMarkdown = getChangelogForDisplay(parsed, settingsManager);\n\n\t\tif (scopedModels.length > 0) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel !== \"off\" ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\tconst fdPath = await ensureTool(\"fd\");\n\t\ttime(\"ensureTool(fd)\");\n\n\t\tprintTimings();\n\t\tawait runInteractiveMode(\n\t\t\tsession,\n\t\t\tVERSION,\n\t\t\tchangelogMarkdown,\n\t\t\tmodelFallbackMessage,\n\t\t\tmodelRegistry.getError(),\n\t\t\tmigratedProviders,\n\t\t\tversionCheckPromise,\n\t\t\tparsed.messages,\n\t\t\tcustomToolsResult.tools,\n\t\t\tcustomToolsResult.setUIContext,\n\t\t\tinitialMessage,\n\t\t\tinitialAttachments,\n\t\t\tfdPath,\n\t\t);\n\t} else {\n\t\tawait runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);\n\t\tstopThemeWatcher();\n\t\tif (process.stdout.writableLength > 0) {\n\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t}\n\t\tprocess.exit(0);\n\t}\n}\n"]}
package/dist/main.js CHANGED
@@ -6,15 +6,16 @@
6
6
  */
7
7
  import { supportsXhigh } from "@mariozechner/pi-ai";
8
8
  import chalk from "chalk";
9
+ import { join } from "path";
9
10
  import { parseArgs, printHelp } from "./cli/args.js";
10
11
  import { processFileArguments } from "./cli/file-processor.js";
11
12
  import { listModels } from "./cli/list-models.js";
12
13
  import { selectSession } from "./cli/session-picker.js";
13
- import { getModelsPath, VERSION } from "./config.js";
14
+ import { getAgentDir, getModelsPath, VERSION } from "./config.js";
15
+ import { AuthStorage } from "./core/auth-storage.js";
14
16
  import { exportFromFile } from "./core/export-html.js";
15
- import { findModel } from "./core/model-config.js";
16
17
  import { resolveModelScope } from "./core/model-resolver.js";
17
- import { configureOAuthStorage, createAgentSession } from "./core/sdk.js";
18
+ import { createAgentSession, discoverAuthStorage, discoverModels } from "./core/sdk.js";
18
19
  import { SessionManager } from "./core/session-manager.js";
19
20
  import { SettingsManager } from "./core/settings-manager.js";
20
21
  import { resolvePromptInput } from "./core/system-prompt.js";
@@ -26,7 +27,7 @@ import { getChangelogPath, getNewEntries, parseChangelog } from "./utils/changel
26
27
  import { ensureTool } from "./utils/tools-manager.js";
27
28
  async function checkForNewVersion(currentVersion) {
28
29
  try {
29
- const response = await fetch("https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest");
30
+ const response = await fetch("https://registry.npmjs.org/@mariozechner/pi -coding-agent/latest");
30
31
  if (!response.ok)
31
32
  return null;
32
33
  const data = (await response.json());
@@ -40,7 +41,7 @@ async function checkForNewVersion(currentVersion) {
40
41
  return null;
41
42
  }
42
43
  }
43
- async function runInteractiveMode(session, version, changelogMarkdown, modelFallbackMessage, versionCheckPromise, initialMessages, customTools, setToolUIContext, initialMessage, initialAttachments, fdPath = null) {
44
+ async function runInteractiveMode(session, version, changelogMarkdown, modelFallbackMessage, modelsJsonError, migratedProviders, versionCheckPromise, initialMessages, customTools, setToolUIContext, initialMessage, initialAttachments, fdPath = null) {
44
45
  const mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);
45
46
  await mode.init();
46
47
  versionCheckPromise.then((newVersion) => {
@@ -49,6 +50,12 @@ async function runInteractiveMode(session, version, changelogMarkdown, modelFall
49
50
  }
50
51
  });
51
52
  mode.renderInitialMessages(session.state);
53
+ if (migratedProviders.length > 0) {
54
+ mode.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
55
+ }
56
+ if (modelsJsonError) {
57
+ mode.showError(`models.json error: ${modelsJsonError}`);
58
+ }
52
59
  if (modelFallbackMessage) {
53
60
  mode.showWarning(modelFallbackMessage);
54
61
  }
@@ -135,7 +142,7 @@ function createSessionManager(parsed, cwd) {
135
142
  // Default case (new session) returns null, SDK will create one
136
143
  return null;
137
144
  }
138
- function buildSessionOptions(parsed, scopedModels, sessionManager) {
145
+ function buildSessionOptions(parsed, scopedModels, sessionManager, modelRegistry) {
139
146
  const options = {};
140
147
  const resolvedSystemPrompt = resolvePromptInput(parsed.systemPrompt, "system prompt");
141
148
  const resolvedAppendPrompt = resolvePromptInput(parsed.appendSystemPrompt, "append system prompt");
@@ -144,11 +151,7 @@ function buildSessionOptions(parsed, scopedModels, sessionManager) {
144
151
  }
145
152
  // Model from CLI
146
153
  if (parsed.provider && parsed.model) {
147
- const { model, error } = findModel(parsed.provider, parsed.model);
148
- if (error) {
149
- console.error(chalk.red(error));
150
- process.exit(1);
151
- }
154
+ const model = modelRegistry.find(parsed.provider, parsed.model);
152
155
  if (!model) {
153
156
  console.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));
154
157
  process.exit(1);
@@ -169,10 +172,8 @@ function buildSessionOptions(parsed, scopedModels, sessionManager) {
169
172
  if (scopedModels.length > 0) {
170
173
  options.scopedModels = scopedModels;
171
174
  }
172
- // API key from CLI
173
- if (parsed.apiKey) {
174
- options.getApiKey = async () => parsed.apiKey;
175
- }
175
+ // API key from CLI - set in authStorage
176
+ // (handled by caller before createAgentSession)
176
177
  // System prompt
177
178
  if (resolvedSystemPrompt && resolvedAppendPrompt) {
178
179
  options.systemPrompt = `${resolvedSystemPrompt}\n\n${resolvedAppendPrompt}`;
@@ -203,8 +204,13 @@ function buildSessionOptions(parsed, scopedModels, sessionManager) {
203
204
  }
204
205
  export async function main(args) {
205
206
  time("start");
206
- configureOAuthStorage();
207
- time("configureOAuthStorage");
207
+ // Migrate legacy oauth.json and settings.json apiKeys to auth.json
208
+ const agentDir = getAgentDir();
209
+ const migratedProviders = AuthStorage.migrateLegacy(join(agentDir, "auth.json"), agentDir);
210
+ // Create AuthStorage and ModelRegistry upfront
211
+ const authStorage = discoverAuthStorage();
212
+ const modelRegistry = discoverModels(authStorage);
213
+ time("discoverModels");
208
214
  const parsed = parseArgs(args);
209
215
  time("parseArgs");
210
216
  if (parsed.version) {
@@ -217,8 +223,7 @@ export async function main(args) {
217
223
  }
218
224
  if (parsed.listModels !== undefined) {
219
225
  const searchPattern = typeof parsed.listModels === "string" ? parsed.listModels : undefined;
220
- const settingsManager = SettingsManager.create(process.cwd());
221
- await listModels(searchPattern, settingsManager);
226
+ await listModels(modelRegistry, searchPattern);
222
227
  return;
223
228
  }
224
229
  if (parsed.export) {
@@ -249,7 +254,7 @@ export async function main(args) {
249
254
  time("initTheme");
250
255
  let scopedModels = [];
251
256
  if (parsed.models && parsed.models.length > 0) {
252
- scopedModels = await resolveModelScope(parsed.models, settingsManager);
257
+ scopedModels = await resolveModelScope(parsed.models, modelRegistry);
253
258
  time("resolveModelScope");
254
259
  }
255
260
  // Create session manager based on CLI flags
@@ -271,7 +276,17 @@ export async function main(args) {
271
276
  }
272
277
  sessionManager = SessionManager.open(selectedPath);
273
278
  }
274
- const sessionOptions = buildSessionOptions(parsed, scopedModels, sessionManager);
279
+ const sessionOptions = buildSessionOptions(parsed, scopedModels, sessionManager, modelRegistry);
280
+ sessionOptions.authStorage = authStorage;
281
+ sessionOptions.modelRegistry = modelRegistry;
282
+ // Handle CLI --api-key as runtime override (not persisted)
283
+ if (parsed.apiKey) {
284
+ if (!sessionOptions.model) {
285
+ console.error(chalk.red("--api-key requires a model to be specified via --provider/--model or -m/--models"));
286
+ process.exit(1);
287
+ }
288
+ authStorage.setRuntimeApiKey(sessionOptions.model.provider, parsed.apiKey);
289
+ }
275
290
  time("buildSessionOptions");
276
291
  const { session, customToolsResult, modelFallbackMessage } = await createAgentSession(sessionOptions);
277
292
  time("createAgentSession");
@@ -313,7 +328,7 @@ export async function main(args) {
313
328
  const fdPath = await ensureTool("fd");
314
329
  time("ensureTool(fd)");
315
330
  printTimings();
316
- await runInteractiveMode(session, VERSION, changelogMarkdown, modelFallbackMessage, versionCheckPromise, parsed.messages, customToolsResult.tools, customToolsResult.setUIContext, initialMessage, initialAttachments, fdPath);
331
+ await runInteractiveMode(session, VERSION, changelogMarkdown, modelFallbackMessage, modelRegistry.getError(), migratedProviders, versionCheckPromise, parsed.messages, customToolsResult.tools, customToolsResult.setUIContext, initialMessage, initialAttachments, fdPath);
317
332
  }
318
333
  else {
319
334
  await runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);