@ai-outfitter/outfitter 0.11.0 → 1.0.2

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 (308) hide show
  1. package/.outfitter/skills/outfitter/SKILL.md +67 -37
  2. package/README.md +63 -35
  3. package/code/enterprise/cli/privateCatalogSettings.cjs +2 -2
  4. package/code/enterprise/pi-extension/privateCatalogOnboarding.js +5 -8
  5. package/code/enterprise/shared/privateCatalogPolicy.cjs +5 -5
  6. package/code/pi-extension/src/outfitter-extension.js +353 -553
  7. package/code/pi-extension/src/outfitter-runtime-extension.js +160 -0
  8. package/dist/agents/AgentLaunch.d.ts +9 -1
  9. package/dist/agents/AgentLaunch.js +19 -0
  10. package/dist/agents/AgentLaunch.js.map +1 -1
  11. package/dist/agents/PiCredentialPersistence.d.ts +6 -0
  12. package/dist/agents/PiCredentialPersistence.js +33 -0
  13. package/dist/agents/PiCredentialPersistence.js.map +1 -0
  14. package/dist/cli/OutfitterCli.js +10 -18
  15. package/dist/cli/OutfitterCli.js.map +1 -1
  16. package/dist/cli/commands/CommandObject.d.ts +0 -5
  17. package/dist/cli/commands/CommandObject.js +1 -4
  18. package/dist/cli/commands/CommandObject.js.map +1 -1
  19. package/dist/cli/commands/DumpCommand.d.ts +19 -0
  20. package/dist/cli/commands/DumpCommand.js +54 -0
  21. package/dist/cli/commands/DumpCommand.js.map +1 -0
  22. package/dist/cli/commands/ListCommand.d.ts +17 -0
  23. package/dist/cli/commands/ListCommand.js +77 -0
  24. package/dist/cli/commands/ListCommand.js.map +1 -0
  25. package/dist/cli/commands/PiRuntimeLaunch.d.ts +8 -0
  26. package/dist/cli/commands/PiRuntimeLaunch.js +47 -0
  27. package/dist/cli/commands/PiRuntimeLaunch.js.map +1 -0
  28. package/dist/cli/commands/ProcessDefaults.d.ts +2 -0
  29. package/dist/cli/commands/ProcessDefaults.js +5 -0
  30. package/dist/cli/commands/ProcessDefaults.js.map +1 -0
  31. package/dist/cli/commands/RunAgentCommand.d.ts +46 -0
  32. package/dist/cli/commands/RunAgentCommand.js +176 -0
  33. package/dist/cli/commands/RunAgentCommand.js.map +1 -0
  34. package/dist/cli/commands/SetupCommand.d.ts +38 -6
  35. package/dist/cli/commands/SetupCommand.js +194 -232
  36. package/dist/cli/commands/SetupCommand.js.map +1 -1
  37. package/dist/cli/commands/ValidateCommand.d.ts +20 -0
  38. package/dist/cli/commands/ValidateCommand.js +54 -0
  39. package/dist/cli/commands/ValidateCommand.js.map +1 -0
  40. package/dist/composer/Composer.d.ts +11 -0
  41. package/dist/composer/Composer.js +72 -0
  42. package/dist/composer/Composer.js.map +1 -0
  43. package/dist/composer/Composition.d.ts +33 -0
  44. package/dist/composer/Composition.js +2 -0
  45. package/dist/composer/Composition.js.map +1 -0
  46. package/dist/dump/Containment.d.ts +8 -0
  47. package/dist/dump/Containment.js +22 -0
  48. package/dist/dump/Containment.js.map +1 -0
  49. package/dist/dump/Dump.d.ts +8 -0
  50. package/dist/dump/Dump.js +182 -0
  51. package/dist/dump/Dump.js.map +1 -0
  52. package/dist/extensions/PiExtensionCache.d.ts +30 -0
  53. package/dist/extensions/PiExtensionCache.js +92 -0
  54. package/dist/extensions/PiExtensionCache.js.map +1 -0
  55. package/dist/fs/TypeConflict.d.ts +6 -0
  56. package/dist/fs/TypeConflict.js +21 -0
  57. package/dist/fs/TypeConflict.js.map +1 -0
  58. package/dist/paths/OutfitterCache.d.ts +6 -0
  59. package/dist/paths/OutfitterCache.js +17 -0
  60. package/dist/paths/OutfitterCache.js.map +1 -0
  61. package/dist/projection/Materialize.d.ts +24 -0
  62. package/dist/projection/Materialize.js +83 -0
  63. package/dist/projection/Materialize.js.map +1 -0
  64. package/dist/projection/ProjectHarness.d.ts +5 -0
  65. package/dist/projection/ProjectHarness.js +77 -0
  66. package/dist/projection/ProjectHarness.js.map +1 -0
  67. package/dist/projection/Projection.d.ts +23 -0
  68. package/dist/projection/Projection.js +2 -0
  69. package/dist/projection/Projection.js.map +1 -0
  70. package/dist/resolver/AgentDefinition.d.ts +30 -0
  71. package/dist/resolver/AgentDefinition.js +123 -0
  72. package/dist/resolver/AgentDefinition.js.map +1 -0
  73. package/dist/resolver/Layer.d.ts +12 -0
  74. package/dist/resolver/Layer.js +30 -0
  75. package/dist/resolver/Layer.js.map +1 -0
  76. package/dist/resolver/Resolver.d.ts +3 -0
  77. package/dist/resolver/Resolver.js +163 -0
  78. package/dist/resolver/Resolver.js.map +1 -0
  79. package/dist/resolver/ResolverContext.d.ts +15 -0
  80. package/dist/resolver/ResolverContext.js +11 -0
  81. package/dist/resolver/ResolverContext.js.map +1 -0
  82. package/dist/resolver/ResolverValidation.d.ts +11 -0
  83. package/dist/resolver/ResolverValidation.js +112 -0
  84. package/dist/resolver/ResolverValidation.js.map +1 -0
  85. package/dist/resolver/Resource.d.ts +88 -0
  86. package/dist/resolver/Resource.js +34 -0
  87. package/dist/resolver/Resource.js.map +1 -0
  88. package/dist/schemas/agent.schema.json +48 -0
  89. package/dist/schemas/settings.schema.json +32 -9
  90. package/dist/settings/Settings.d.ts +34 -5
  91. package/dist/settings/Settings.js +3 -1
  92. package/dist/settings/Settings.js.map +1 -1
  93. package/dist/settings/SettingsLoader.d.ts +1 -1
  94. package/dist/settings/SettingsLoader.js +23 -22
  95. package/dist/settings/SettingsLoader.js.map +1 -1
  96. package/dist/settings/SettingsMerger.js +12 -10
  97. package/dist/settings/SettingsMerger.js.map +1 -1
  98. package/dist/setup/DefaultCatalog.d.ts +20 -0
  99. package/dist/setup/DefaultCatalog.js +89 -0
  100. package/dist/setup/DefaultCatalog.js.map +1 -0
  101. package/dist/setup/Setup.d.ts +43 -0
  102. package/dist/setup/Setup.js +261 -0
  103. package/dist/setup/Setup.js.map +1 -0
  104. package/dist/skills/SkillDocument.d.ts +6 -1
  105. package/dist/skills/SkillDocument.js.map +1 -1
  106. package/dist/sources/SourceCache.d.ts +19 -0
  107. package/dist/{profiles/ProfileCache.js → sources/SourceCache.js} +22 -18
  108. package/dist/sources/SourceCache.js.map +1 -0
  109. package/dist/validation/SchemaValidator.d.ts +1 -1
  110. package/dist/validation/SchemaValidator.js +4 -13
  111. package/dist/validation/SchemaValidator.js.map +1 -1
  112. package/docs/architecture/state_writeback_strategy.md +54 -122
  113. package/docs/documentation/README.md +33 -12
  114. package/docs/documentation/actions.md +35 -52
  115. package/docs/documentation/agents.md +109 -0
  116. package/docs/documentation/best-practices.md +25 -63
  117. package/docs/documentation/catalogs.md +126 -0
  118. package/docs/documentation/cli.md +39 -41
  119. package/docs/documentation/concepts.md +66 -23
  120. package/docs/documentation/dump-and-bake.md +30 -0
  121. package/docs/documentation/first-time-cli-agent-users.md +8 -8
  122. package/docs/documentation/getting-started.md +28 -8
  123. package/docs/documentation/hooks.md +20 -0
  124. package/docs/documentation/iterating-on-profiles.md +56 -70
  125. package/docs/documentation/local-development.md +84 -0
  126. package/docs/documentation/migration.md +39 -0
  127. package/docs/documentation/personas.md +41 -0
  128. package/docs/documentation/porting-claude.md +54 -0
  129. package/docs/documentation/profiles.md +16 -169
  130. package/docs/documentation/settings.md +61 -0
  131. package/docs/documentation/skills.md +93 -334
  132. package/docs/documentation/state.md +24 -62
  133. package/docs/documentation/subagents.md +37 -0
  134. package/docs/documentation/support-matrix.md +40 -35
  135. package/docs/documentation/switching-to-outfitter.md +75 -81
  136. package/docs/documentation/tasks.md +13 -0
  137. package/docs/documentation/usecases/engineering.md +67 -84
  138. package/docs/documentation/usecases/organization-profile-catalog.md +83 -111
  139. package/docs/documentation/usecases/persona-reviews.md +133 -139
  140. package/docs/philosophy.md +2 -2
  141. package/package.json +3 -3
  142. package/src/schemas/agent.schema.json +48 -0
  143. package/src/schemas/settings.schema.json +32 -9
  144. package/dist/agents/AdapterProfileControls.d.ts +0 -21
  145. package/dist/agents/AdapterProfileControls.js +0 -76
  146. package/dist/agents/AdapterProfileControls.js.map +0 -1
  147. package/dist/agents/AdapterStatePaths.d.ts +0 -12
  148. package/dist/agents/AdapterStatePaths.js +0 -46
  149. package/dist/agents/AdapterStatePaths.js.map +0 -1
  150. package/dist/agents/AgentAdapter.d.ts +0 -44
  151. package/dist/agents/AgentAdapter.js +0 -2
  152. package/dist/agents/AgentAdapter.js.map +0 -1
  153. package/dist/agents/AgentRegistry.d.ts +0 -6
  154. package/dist/agents/AgentRegistry.js +0 -17
  155. package/dist/agents/AgentRegistry.js.map +0 -1
  156. package/dist/agents/LaunchResources.d.ts +0 -17
  157. package/dist/agents/LaunchResources.js +0 -61
  158. package/dist/agents/LaunchResources.js.map +0 -1
  159. package/dist/agents/OutfitterSkill.d.ts +0 -11
  160. package/dist/agents/OutfitterSkill.js +0 -128
  161. package/dist/agents/OutfitterSkill.js.map +0 -1
  162. package/dist/agents/ResourceIdentity.d.ts +0 -2
  163. package/dist/agents/ResourceIdentity.js +0 -51
  164. package/dist/agents/ResourceIdentity.js.map +0 -1
  165. package/dist/agents/claude/ClaudeAdapter.d.ts +0 -2
  166. package/dist/agents/claude/ClaudeAdapter.js +0 -148
  167. package/dist/agents/claude/ClaudeAdapter.js.map +0 -1
  168. package/dist/agents/claude/ClaudeCompositeProfileWriter.d.ts +0 -5
  169. package/dist/agents/claude/ClaudeCompositeProfileWriter.js +0 -7
  170. package/dist/agents/claude/ClaudeCompositeProfileWriter.js.map +0 -1
  171. package/dist/agents/pi/PiAdapter.d.ts +0 -2
  172. package/dist/agents/pi/PiAdapter.js +0 -363
  173. package/dist/agents/pi/PiAdapter.js.map +0 -1
  174. package/dist/agents/pi/PiArgs.d.ts +0 -2
  175. package/dist/agents/pi/PiArgs.js +0 -15
  176. package/dist/agents/pi/PiArgs.js.map +0 -1
  177. package/dist/agents/pi/PiCompositeProfileWriter.d.ts +0 -5
  178. package/dist/agents/pi/PiCompositeProfileWriter.js +0 -7
  179. package/dist/agents/pi/PiCompositeProfileWriter.js.map +0 -1
  180. package/dist/agents/pi/PiExtensionCache.d.ts +0 -12
  181. package/dist/agents/pi/PiExtensionCache.js +0 -195
  182. package/dist/agents/pi/PiExtensionCache.js.map +0 -1
  183. package/dist/agents/pi/PiMcpConfig.d.ts +0 -2
  184. package/dist/agents/pi/PiMcpConfig.js +0 -114
  185. package/dist/agents/pi/PiMcpConfig.js.map +0 -1
  186. package/dist/agents/pi/PiSettingsMergePolicy.d.ts +0 -17
  187. package/dist/agents/pi/PiSettingsMergePolicy.js +0 -59
  188. package/dist/agents/pi/PiSettingsMergePolicy.js.map +0 -1
  189. package/dist/agents/pi/PiSkillSources.d.ts +0 -8
  190. package/dist/agents/pi/PiSkillSources.js +0 -73
  191. package/dist/agents/pi/PiSkillSources.js.map +0 -1
  192. package/dist/cli/commands/FirstRunWelcomeProfile.d.ts +0 -11
  193. package/dist/cli/commands/FirstRunWelcomeProfile.js +0 -110
  194. package/dist/cli/commands/FirstRunWelcomeProfile.js.map +0 -1
  195. package/dist/cli/commands/PiLoginLaunch.d.ts +0 -22
  196. package/dist/cli/commands/PiLoginLaunch.js +0 -170
  197. package/dist/cli/commands/PiLoginLaunch.js.map +0 -1
  198. package/dist/cli/commands/RunCommand.d.ts +0 -36
  199. package/dist/cli/commands/RunCommand.js +0 -345
  200. package/dist/cli/commands/RunCommand.js.map +0 -1
  201. package/dist/cli/commands/SyncCommand.d.ts +0 -46
  202. package/dist/cli/commands/SyncCommand.js +0 -244
  203. package/dist/cli/commands/SyncCommand.js.map +0 -1
  204. package/dist/cli/commands/WelcomeCommand.d.ts +0 -56
  205. package/dist/cli/commands/WelcomeCommand.js +0 -224
  206. package/dist/cli/commands/WelcomeCommand.js.map +0 -1
  207. package/dist/cli/commands/assets/outfitter-ascii.txt +0 -5
  208. package/dist/cli/commands/profile/Command.d.ts +0 -7
  209. package/dist/cli/commands/profile/Command.js +0 -24
  210. package/dist/cli/commands/profile/Command.js.map +0 -1
  211. package/dist/cli/commands/profile/CreateCommand.d.ts +0 -19
  212. package/dist/cli/commands/profile/CreateCommand.js +0 -115
  213. package/dist/cli/commands/profile/CreateCommand.js.map +0 -1
  214. package/dist/cli/commands/profile/LintCommand.d.ts +0 -19
  215. package/dist/cli/commands/profile/LintCommand.js +0 -155
  216. package/dist/cli/commands/profile/LintCommand.js.map +0 -1
  217. package/dist/cli/commands/profile/ListCommand.d.ts +0 -19
  218. package/dist/cli/commands/profile/ListCommand.js +0 -91
  219. package/dist/cli/commands/profile/ListCommand.js.map +0 -1
  220. package/dist/cli/commands/profile/Shared.d.ts +0 -9
  221. package/dist/cli/commands/profile/Shared.js +0 -10
  222. package/dist/cli/commands/profile/Shared.js.map +0 -1
  223. package/dist/cli/commands/run/RunFirstRunOnboarding.d.ts +0 -7
  224. package/dist/cli/commands/run/RunFirstRunOnboarding.js +0 -52
  225. package/dist/cli/commands/run/RunFirstRunOnboarding.js.map +0 -1
  226. package/dist/cli/commands/run/RunLaunchSummary.d.ts +0 -2
  227. package/dist/cli/commands/run/RunLaunchSummary.js +0 -35
  228. package/dist/cli/commands/run/RunLaunchSummary.js.map +0 -1
  229. package/dist/cli/commands/run/RunProfileResolution.d.ts +0 -39
  230. package/dist/cli/commands/run/RunProfileResolution.js +0 -128
  231. package/dist/cli/commands/run/RunProfileResolution.js.map +0 -1
  232. package/dist/cli/commands/run/RunStateWritePrompt.d.ts +0 -2
  233. package/dist/cli/commands/run/RunStateWritePrompt.js +0 -29
  234. package/dist/cli/commands/run/RunStateWritePrompt.js.map +0 -1
  235. package/dist/cli/commands/setup/SetupPrompts.d.ts +0 -14
  236. package/dist/cli/commands/setup/SetupPrompts.js +0 -296
  237. package/dist/cli/commands/setup/SetupPrompts.js.map +0 -1
  238. package/dist/cli/commands/setup/SetupSourceImport.d.ts +0 -5
  239. package/dist/cli/commands/setup/SetupSourceImport.js +0 -177
  240. package/dist/cli/commands/setup/SetupSourceImport.js.map +0 -1
  241. package/dist/cli/commands/setup/SetupSourceLaunch.d.ts +0 -4
  242. package/dist/cli/commands/setup/SetupSourceLaunch.js +0 -65
  243. package/dist/cli/commands/setup/SetupSourceLaunch.js.map +0 -1
  244. package/dist/cli/commands/setup/SetupStarterSource.d.ts +0 -21
  245. package/dist/cli/commands/setup/SetupStarterSource.js +0 -133
  246. package/dist/cli/commands/setup/SetupStarterSource.js.map +0 -1
  247. package/dist/cli/commands/setup/SetupTypes.d.ts +0 -91
  248. package/dist/cli/commands/setup/SetupTypes.js +0 -26
  249. package/dist/cli/commands/setup/SetupTypes.js.map +0 -1
  250. package/dist/compositeProfile/CompositeProfile.d.ts +0 -8
  251. package/dist/compositeProfile/CompositeProfile.js +0 -6
  252. package/dist/compositeProfile/CompositeProfile.js.map +0 -1
  253. package/dist/compositeProfile/CompositeProfileAssembler.d.ts +0 -12
  254. package/dist/compositeProfile/CompositeProfileAssembler.js +0 -32
  255. package/dist/compositeProfile/CompositeProfileAssembler.js.map +0 -1
  256. package/dist/compositeProfile/CompositeProfileCleanup.d.ts +0 -9
  257. package/dist/compositeProfile/CompositeProfileCleanup.js +0 -87
  258. package/dist/compositeProfile/CompositeProfileCleanup.js.map +0 -1
  259. package/dist/compositeProfile/CompositeProfileFile.d.ts +0 -16
  260. package/dist/compositeProfile/CompositeProfileFile.js +0 -16
  261. package/dist/compositeProfile/CompositeProfileFile.js.map +0 -1
  262. package/dist/compositeProfile/CompositeProfileTemplate.d.ts +0 -15
  263. package/dist/compositeProfile/CompositeProfileTemplate.js +0 -65
  264. package/dist/compositeProfile/CompositeProfileTemplate.js.map +0 -1
  265. package/dist/compositeProfile/CompositeProfileWatcher.d.ts +0 -18
  266. package/dist/compositeProfile/CompositeProfileWatcher.js +0 -46
  267. package/dist/compositeProfile/CompositeProfileWatcher.js.map +0 -1
  268. package/dist/compositeProfile/StatePersistence.d.ts +0 -39
  269. package/dist/compositeProfile/StatePersistence.js +0 -249
  270. package/dist/compositeProfile/StatePersistence.js.map +0 -1
  271. package/dist/fs/SafeSymlink.d.ts +0 -13
  272. package/dist/fs/SafeSymlink.js +0 -50
  273. package/dist/fs/SafeSymlink.js.map +0 -1
  274. package/dist/profiles/Profile.d.ts +0 -60
  275. package/dist/profiles/Profile.js +0 -7
  276. package/dist/profiles/Profile.js.map +0 -1
  277. package/dist/profiles/ProfileCache.d.ts +0 -8
  278. package/dist/profiles/ProfileCache.js.map +0 -1
  279. package/dist/profiles/ProfileLoader.d.ts +0 -28
  280. package/dist/profiles/ProfileLoader.js +0 -299
  281. package/dist/profiles/ProfileLoader.js.map +0 -1
  282. package/dist/profiles/ProfileMerger.d.ts +0 -19
  283. package/dist/profiles/ProfileMerger.js +0 -112
  284. package/dist/profiles/ProfileMerger.js.map +0 -1
  285. package/dist/profiles/ProfileSource.d.ts +0 -35
  286. package/dist/profiles/ProfileSource.js +0 -13
  287. package/dist/profiles/ProfileSource.js.map +0 -1
  288. package/dist/profiles/PromptIncludes.d.ts +0 -32
  289. package/dist/profiles/PromptIncludes.js +0 -147
  290. package/dist/profiles/PromptIncludes.js.map +0 -1
  291. package/dist/prompts/SystemPromptExport.d.ts +0 -16
  292. package/dist/prompts/SystemPromptExport.js +0 -81
  293. package/dist/prompts/SystemPromptExport.js.map +0 -1
  294. package/dist/schemas/profile-source.schema.json +0 -29
  295. package/dist/schemas/profile.schema.json +0 -200
  296. package/dist/skills/ProfileSkillResolution.d.ts +0 -21
  297. package/dist/skills/ProfileSkillResolution.js +0 -88
  298. package/dist/skills/ProfileSkillResolution.js.map +0 -1
  299. package/dist/skills/SkillCatalog.d.ts +0 -41
  300. package/dist/skills/SkillCatalog.js +0 -119
  301. package/dist/skills/SkillCatalog.js.map +0 -1
  302. package/dist/skills/SkillResolution.d.ts +0 -34
  303. package/dist/skills/SkillResolution.js +0 -369
  304. package/dist/skills/SkillResolution.js.map +0 -1
  305. package/docs/documentation/profile-repository.md +0 -179
  306. package/src/schemas/SchemaDocument.ts +0 -20
  307. package/src/schemas/profile-source.schema.json +0 -29
  308. package/src/schemas/profile.schema.json +0 -200
@@ -1,88 +0,0 @@
1
- // Resolves a profile's selected skill IDs into generated skill sources before adapter launch.
2
- import { join } from 'node:path';
3
- import { inferProfileIncludeSourceRoot } from '../profiles/PromptIncludes.js';
4
- import { skillControlEntryIdentity } from '../agents/LaunchResources.js';
5
- import { discoverSkillCatalog } from './SkillCatalog.js';
6
- import { resolveSkillEntries, } from './SkillResolution.js';
7
- /**
8
- * Resolves catalog skill IDs in `controls.skills` (generic and adapter-scoped) to
9
- * generated skill directories, so adapters keep receiving plain path sources.
10
- */
11
- export const resolveProfileSkillControls = (input) => {
12
- const catalog = discoverSkillCatalog({
13
- projectDirectory: input.projectDirectory,
14
- profileLayers: input.profileLayers,
15
- sourcePaths: input.sourcePaths,
16
- });
17
- const diagnostics = [];
18
- const outputDirectory = input.outputDirectory === undefined ? undefined : join(input.outputDirectory, 'outfitter', 'skills');
19
- // One skill ID materializes once; selecting it from several control scopes reuses
20
- // the same generated directory, and conflicting reference sets are an error.
21
- const materializationCache = new Map();
22
- const resolveEntries = (entries, scope) => {
23
- const resolution = resolveSkillEntries({
24
- entries: entries.map((entry) => toSkillEntryInput(entry, scope, input.profileLayers)),
25
- catalog,
26
- projectDirectory: input.projectDirectory,
27
- outputDirectory,
28
- materializationCache,
29
- });
30
- diagnostics.push(...resolution.diagnostics);
31
- return resolution.sources;
32
- };
33
- const controls = resolveSkillControls(input.profile.controls, resolveEntries);
34
- diagnostics.push(...createShadowedSkillDiagnostics(catalog, input.profile.controls));
35
- return { profile: { ...input.profile, controls }, diagnostics };
36
- };
37
- const resolveSkillControls = (controls, resolveEntries) => ({
38
- ...controls,
39
- ...(controls.skills === undefined ? {} : { skills: resolveEntries(controls.skills, 'skills') }),
40
- ...(controls.pi?.skills === undefined
41
- ? {}
42
- : { pi: { ...controls.pi, skills: resolveEntries(controls.pi.skills, 'pi') } }),
43
- ...(controls.claude?.skills === undefined
44
- ? {}
45
- : { claude: { ...controls.claude, skills: resolveEntries(controls.claude.skills, 'claude') } }),
46
- });
47
- /**
48
- * Profile-added `file` references resolve from the repository containing the
49
- * declaring profile. The lookup stays within the entry's own control scope so an
50
- * object selection in another scope cannot supply the wrong repository root.
51
- */
52
- const toSkillEntryInput = (entry, scope, profileLayers) => {
53
- if (typeof entry === 'string') {
54
- return { entry };
55
- }
56
- const identity = skillControlEntryIdentity(entry);
57
- const declaringLayer = [...profileLayers]
58
- .reverse()
59
- .find((layer) => readScopedSkillEntries(layer.profile.controls, scope).some((layerEntry) => typeof layerEntry !== 'string' && skillControlEntryIdentity(layerEntry) === identity));
60
- return {
61
- entry,
62
- profileReferenceRoot: declaringLayer === undefined
63
- ? undefined
64
- : inferProfileIncludeSourceRoot({
65
- sourceRootPath: declaringLayer.sourceRootPath,
66
- profilePath: declaringLayer.profilePath,
67
- }),
68
- };
69
- };
70
- const readScopedSkillEntries = (controls, scope) => (scope === 'skills' ? controls.skills : controls[scope]?.skills) ?? [];
71
- const readLayerSkillEntries = (controls) => [
72
- ...(controls.skills ?? []),
73
- ...(controls.pi?.skills ?? []),
74
- ...(controls.claude?.skills ?? []),
75
- ];
76
- /** Reports shadowing only for IDs the profile actually selects. */
77
- const createShadowedSkillDiagnostics = (catalog, controls) => {
78
- const selectedIds = new Set(readLayerSkillEntries(controls).map((entry) => (typeof entry === 'string' ? entry : entry.id)));
79
- return catalog.shadowed
80
- .filter((shadow) => selectedIds.has(shadow.selected.id))
81
- .map((shadow) => ({
82
- severity: 'warning',
83
- path: shadow.shadowedDirectory,
84
- message: `Skill ID '${shadow.selected.id}' is supplied by ${shadow.selected.origin} ('${shadow.selected.directory}') ` +
85
- `and shadows '${shadow.shadowedDirectory}'.`,
86
- }));
87
- };
88
- //# sourceMappingURL=ProfileSkillResolution.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ProfileSkillResolution.js","sourceRoot":"","sources":["../../src/skills/ProfileSkillResolution.ts"],"names":[],"mappings":"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"}
@@ -1,41 +0,0 @@
1
- export interface SkillCatalogRoot {
2
- /** Directory scanned for `<skill-id>/SKILL.md` entries. */
3
- readonly skillsDirectory: string;
4
- /** Root that skill `file` references resolve from (the repository containing the skill). */
5
- readonly referenceRoot: string;
6
- /** Human-readable origin for shadowing diagnostics. */
7
- readonly origin: string;
8
- }
9
- export interface SkillCatalogEntry {
10
- readonly id: string;
11
- readonly directory: string;
12
- readonly skillPath: string;
13
- readonly referenceRoot: string;
14
- readonly origin: string;
15
- }
16
- export interface SkillCatalog {
17
- readonly entries: ReadonlyMap<string, SkillCatalogEntry>;
18
- readonly shadowed: readonly {
19
- readonly selected: SkillCatalogEntry;
20
- readonly shadowedDirectory: string;
21
- }[];
22
- }
23
- export interface SkillCatalogInput {
24
- readonly projectDirectory?: string;
25
- /** Directory-profile resource roots contributing bundled `skills/` folders. */
26
- readonly profileLayers?: readonly {
27
- readonly sourceRootPath?: string;
28
- readonly resourceRootPath?: string;
29
- }[];
30
- /** Materialized configured profile-source paths, in configured (precedence) order. */
31
- readonly sourcePaths?: readonly string[];
32
- }
33
- /**
34
- * Builds the skill ID catalog following layer precedence: project-local, project,
35
- * bundled directory-profile skills (highest-precedence layer first), then
36
- * configured sources (later entries outrank earlier ones, matching profile
37
- * precedence). The first root supplying an ID wins; later roots are reported as
38
- * shadowed.
39
- */
40
- export declare const discoverSkillCatalog: (input: SkillCatalogInput) => SkillCatalog;
41
- export declare const createSkillCatalogRoots: (input: SkillCatalogInput) => readonly SkillCatalogRoot[];
@@ -1,119 +0,0 @@
1
- // Discovers catalog skills across project, directory-profile, and configured-source roots.
2
- import { existsSync, readdirSync, statSync } from 'node:fs';
3
- import { basename, dirname, join } from 'node:path';
4
- import { inferProfileIncludeSourceRoot } from '../profiles/PromptIncludes.js';
5
- import { isValidSkillId } from './SkillDocument.js';
6
- /**
7
- * Builds the skill ID catalog following layer precedence: project-local, project,
8
- * bundled directory-profile skills (highest-precedence layer first), then
9
- * configured sources (later entries outrank earlier ones, matching profile
10
- * precedence). The first root supplying an ID wins; later roots are reported as
11
- * shadowed.
12
- */
13
- export const discoverSkillCatalog = (input) => {
14
- const entries = new Map();
15
- const shadowed = [];
16
- for (const root of createSkillCatalogRoots(input)) {
17
- for (const entry of readSkillCatalogRootEntries(root)) {
18
- const selected = entries.get(entry.id);
19
- if (selected === undefined) {
20
- entries.set(entry.id, entry);
21
- }
22
- else if (selected.directory !== entry.directory) {
23
- shadowed.push({ selected, shadowedDirectory: entry.directory });
24
- }
25
- }
26
- }
27
- return { entries, shadowed };
28
- };
29
- export const createSkillCatalogRoots = (input) => {
30
- const roots = [];
31
- if (input.projectDirectory !== undefined) {
32
- roots.push({
33
- skillsDirectory: join(input.projectDirectory, '.outfitter', 'local', 'skills'),
34
- referenceRoot: input.projectDirectory,
35
- origin: 'project-local skills',
36
- }, {
37
- skillsDirectory: join(input.projectDirectory, '.outfitter', 'skills'),
38
- referenceRoot: input.projectDirectory,
39
- origin: 'project skills',
40
- });
41
- }
42
- // Profile layers arrive lowest-precedence first (see ProfileMerger); reverse so
43
- // the selected profile's bundled skill outranks an inherited base profile's.
44
- for (const layer of [...(input.profileLayers ?? [])].reverse()) {
45
- if (layer.resourceRootPath === undefined) {
46
- continue;
47
- }
48
- roots.push({
49
- skillsDirectory: join(layer.resourceRootPath, 'skills'),
50
- referenceRoot: inferProfileIncludeSourceRoot({
51
- sourceRootPath: layer.sourceRootPath,
52
- profilePath: join(layer.resourceRootPath, 'profile.yml'),
53
- /* v8 ignore next -- directory profiles always sit under a source root. */
54
- }) ?? layer.resourceRootPath,
55
- origin: `profile '${basename(layer.resourceRootPath)}' skills`,
56
- });
57
- }
58
- // Later configured sources outrank earlier ones for profiles; mirror that here.
59
- for (const sourcePath of [...(input.sourcePaths ?? [])].reverse()) {
60
- const catalogRoot = resolveCatalogRoot(sourcePath);
61
- roots.push({
62
- skillsDirectory: join(catalogRoot, 'skills'),
63
- referenceRoot: basename(catalogRoot) === '.outfitter' ? dirname(catalogRoot) : catalogRoot,
64
- origin: `source '${sourcePath}'`,
65
- });
66
- }
67
- return dedupeSkillCatalogRoots(roots);
68
- };
69
- /**
70
- * The catalog root is the directory containing `profiles/`. A source path that
71
- * points directly at the profiles directory resolves to its parent.
72
- */
73
- const resolveCatalogRoot = (sourcePath) => basename(sourcePath) === 'profiles' ? dirname(sourcePath) : sourcePath;
74
- const dedupeSkillCatalogRoots = (roots) => {
75
- const seen = new Set();
76
- return roots.filter((root) => {
77
- if (seen.has(root.skillsDirectory)) {
78
- return false;
79
- }
80
- seen.add(root.skillsDirectory);
81
- return true;
82
- });
83
- };
84
- const readSkillCatalogRootEntries = (root) => readOptionalDirectoryNames(root.skillsDirectory)
85
- .filter((name) => isValidSkillId(name) && existsSync(join(root.skillsDirectory, name, 'SKILL.md')))
86
- .sort()
87
- .map((name) => ({
88
- id: name,
89
- directory: join(root.skillsDirectory, name),
90
- skillPath: join(root.skillsDirectory, name, 'SKILL.md'),
91
- referenceRoot: root.referenceRoot,
92
- origin: root.origin,
93
- }));
94
- const readOptionalDirectoryNames = (directory) => {
95
- try {
96
- return readdirSync(directory, { withFileTypes: true })
97
- .filter((entry) => entry.isDirectory() || isSymlinkedDirectory(join(directory, entry.name), entry.isSymbolicLink()))
98
- .map((entry) => entry.name);
99
- }
100
- catch (error) {
101
- if (isMissingPathError(error)) {
102
- return [];
103
- }
104
- throw new Error(`Could not read skills directory '${directory}': ${String(error)}`, { cause: error });
105
- }
106
- };
107
- const isSymlinkedDirectory = (path, isSymbolicLink) => {
108
- if (!isSymbolicLink) {
109
- return false;
110
- }
111
- try {
112
- return statSync(path).isDirectory();
113
- }
114
- catch {
115
- return false;
116
- }
117
- };
118
- const isMissingPathError = (error) => error !== null && typeof error === 'object' && 'code' in error && error.code === 'ENOENT';
119
- //# sourceMappingURL=SkillCatalog.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SkillCatalog.js","sourceRoot":"","sources":["../../src/skills/SkillCatalog.ts"],"names":[],"mappings":"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"}
@@ -1,34 +0,0 @@
1
- import type { SkillControlEntry } from '../profiles/Profile.js';
2
- import type { SkillCatalog } from './SkillCatalog.js';
3
- export interface SkillResolutionDiagnostic {
4
- readonly severity: 'error' | 'warning';
5
- readonly path: string;
6
- readonly message: string;
7
- }
8
- export interface SkillEntryInput {
9
- readonly entry: SkillControlEntry;
10
- /** Root for `file` references appended by the declaring profile. */
11
- readonly profileReferenceRoot?: string;
12
- }
13
- /**
14
- * Shared across control scopes so one skill ID materializes once; a second
15
- * selection reuses the generated directory, and differing reference sets error.
16
- */
17
- export type SkillMaterializationCache = Map<string, {
18
- readonly referencesKey: string;
19
- readonly generatedDirectory?: string;
20
- }>;
21
- export interface SkillResolutionInput {
22
- readonly entries: readonly SkillEntryInput[];
23
- readonly catalog: SkillCatalog;
24
- readonly projectDirectory?: string;
25
- /** When set, resolved skills are copied here as `<id>/` with materialized references. */
26
- readonly outputDirectory?: string;
27
- readonly materializationCache?: SkillMaterializationCache;
28
- }
29
- export interface SkillResolutionResult {
30
- /** Skill sources in entry order: generated directories for resolved IDs, original strings otherwise. */
31
- readonly sources: readonly string[];
32
- readonly diagnostics: readonly SkillResolutionDiagnostic[];
33
- }
34
- export declare const resolveSkillEntries: (input: SkillResolutionInput) => SkillResolutionResult;
@@ -1,369 +0,0 @@
1
- // Resolves selected skill IDs, validates references, and materializes generated skills.
2
- import { copyFileSync, cpSync, existsSync, globSync, mkdirSync, readdirSync, realpathSync, statSync, } from 'node:fs';
3
- import { basename, isAbsolute, join, relative, sep } from 'node:path';
4
- import { isSkillDocumentIssue, isValidSkillId, readSkillDocument, skillMaterializationSections, } from './SkillDocument.js';
5
- export const resolveSkillEntries = (input) => {
6
- const sources = [];
7
- const diagnostics = [];
8
- for (const entryInput of input.entries) {
9
- const outcome = resolveSkillEntry(input, entryInput);
10
- sources.push(...outcome.sources);
11
- diagnostics.push(...outcome.diagnostics);
12
- }
13
- return { sources, diagnostics };
14
- };
15
- const resolveSkillEntry = (input, entryInput) => {
16
- const { entry, profileReferenceRoot } = entryInput;
17
- const selection = typeof entry === 'string' ? { id: entry } : entry;
18
- const catalogEntry = input.catalog.entries.get(selection.id);
19
- if (catalogEntry === undefined) {
20
- return resolveUncataloguedEntry(entry, selection.id);
21
- }
22
- const references = selection.references ?? [];
23
- const referencesKey = JSON.stringify(references);
24
- const cached = input.materializationCache?.get(selection.id);
25
- if (cached !== undefined) {
26
- return resolveCachedEntry(cached, referencesKey, selection.id);
27
- }
28
- const resolved = resolveCatalogSkill({
29
- catalogEntry,
30
- references,
31
- profileReferenceRoot,
32
- projectDirectory: input.projectDirectory,
33
- outputDirectory: input.outputDirectory,
34
- });
35
- input.materializationCache?.set(selection.id, {
36
- referencesKey,
37
- generatedDirectory: resolved.generatedDirectory,
38
- });
39
- return { sources: toEntrySources(resolved.generatedDirectory), diagnostics: resolved.diagnostics };
40
- };
41
- const resolveCachedEntry = (cached, referencesKey, id) => {
42
- if (cached.referencesKey !== referencesKey) {
43
- return {
44
- sources: [],
45
- diagnostics: [
46
- {
47
- severity: 'error',
48
- path: `controls.skills/${id}`,
49
- message: `Skill '${id}' is selected with conflicting reference sets across control scopes.`,
50
- },
51
- ],
52
- };
53
- }
54
- return { sources: toEntrySources(cached.generatedDirectory), diagnostics: [] };
55
- };
56
- const toEntrySources = (generatedDirectory) => generatedDirectory === undefined ? [] : [generatedDirectory];
57
- const resolveUncataloguedEntry = (entry, id) => {
58
- if (typeof entry !== 'string') {
59
- return {
60
- sources: [],
61
- diagnostics: [
62
- {
63
- severity: 'error',
64
- path: `controls.skills/${id}`,
65
- message: `Skill ID '${id}' does not resolve in any skills directory.`,
66
- },
67
- ],
68
- };
69
- }
70
- if (isValidSkillId(entry)) {
71
- return {
72
- sources: [entry],
73
- diagnostics: [
74
- {
75
- severity: 'warning',
76
- path: `controls.skills/${entry}`,
77
- message: `Skill '${entry}' does not resolve in any skills directory; passing it through as a path source.`,
78
- },
79
- ],
80
- };
81
- }
82
- return { sources: [entry], diagnostics: [] };
83
- };
84
- const resolveCatalogSkill = (input) => {
85
- const { catalogEntry } = input;
86
- const diagnostics = [];
87
- const document = readSkillDocument(catalogEntry.skillPath);
88
- if (isSkillDocumentIssue(document)) {
89
- return { diagnostics: [{ severity: 'error', path: document.path, message: document.message }] };
90
- }
91
- if (document.name !== catalogEntry.id) {
92
- return {
93
- diagnostics: [
94
- {
95
- severity: 'error',
96
- path: catalogEntry.skillPath,
97
- message: `SKILL.md 'name' ('${document.name}') must match its directory name ('${catalogEntry.id}').`,
98
- },
99
- ],
100
- };
101
- }
102
- const materializations = planSkillMaterializations(input, document, diagnostics);
103
- if (diagnostics.some((diagnostic) => diagnostic.severity === 'error') || input.outputDirectory === undefined) {
104
- return { diagnostics };
105
- }
106
- const generatedDirectory = join(input.outputDirectory, catalogEntry.id);
107
- cpSync(catalogEntry.directory, generatedDirectory, { recursive: true });
108
- for (const materialization of materializations) {
109
- const sectionDirectory = join(generatedDirectory, materialization.section);
110
- mkdirSync(sectionDirectory, { recursive: true });
111
- const destinationPath = join(sectionDirectory, materialization.destinationName);
112
- // Both copies preserve the source mode, so materialized scripts stay
113
- // executable. Directory targets dereference symlinks that the contained
114
- // scan already validated, so the generated skill is self-contained.
115
- if (materialization.kind === 'directory') {
116
- copyDirectoryDereferenced(materialization.sourcePath, destinationPath);
117
- }
118
- else {
119
- copyFileSync(materialization.sourcePath, destinationPath);
120
- }
121
- }
122
- return { generatedDirectory, diagnostics };
123
- };
124
- const planSkillMaterializations = (input, document, diagnostics) => {
125
- const materializations = [];
126
- for (const section of skillMaterializationSections) {
127
- const entries = [
128
- ...document[section].map((reference) => ({ reference, fileRoot: input.catalogEntry.referenceRoot })),
129
- // Profile-added references resolve `file` from the declaring profile's repository.
130
- ...(section === 'references'
131
- ? input.references.map((reference) => ({
132
- reference,
133
- fileRoot: input.profileReferenceRoot ?? input.projectDirectory ?? input.catalogEntry.referenceRoot,
134
- }))
135
- : []),
136
- ];
137
- materializations.push(...planSectionMaterializations(input, section, entries, diagnostics));
138
- }
139
- return materializations;
140
- };
141
- const planSectionMaterializations = (input, section, entries, diagnostics) => {
142
- const materializations = [];
143
- const destinationSources = new Map();
144
- for (const { reference, fileRoot } of entries) {
145
- const outcomes = resolveReferenceMaterializations({
146
- reference,
147
- section,
148
- fileRoot,
149
- projectDirectory: input.projectDirectory,
150
- skillPath: input.catalogEntry.skillPath,
151
- });
152
- for (const resolved of outcomes) {
153
- if ('message' in resolved) {
154
- diagnostics.push(resolved);
155
- continue;
156
- }
157
- const collision = describeDestinationCollision(input, section, resolved, destinationSources);
158
- if (collision !== undefined) {
159
- diagnostics.push({ severity: 'error', path: input.catalogEntry.skillPath, message: collision });
160
- continue;
161
- }
162
- destinationSources.set(resolved.destinationName, resolved.sourcePath);
163
- materializations.push(resolved);
164
- }
165
- }
166
- return materializations;
167
- };
168
- /** Reports a destination claimed twice or already shipped inside the skill directory. */
169
- const describeDestinationCollision = (input, section, resolved, destinationSources) => {
170
- const previousSource = destinationSources.get(resolved.destinationName);
171
- if (previousSource !== undefined) {
172
- return (`Destination '${section}/${resolved.destinationName}' is declared more than once ` +
173
- `(by '${previousSource}' and '${resolved.sourcePath}').`);
174
- }
175
- // Checked against the source skill directory during planning so lint reports
176
- // shipped-file collisions without materializing anything.
177
- if (existsSync(join(input.catalogEntry.directory, section, resolved.destinationName))) {
178
- return (`Destination '${section}/${resolved.destinationName}' collides with a file ` +
179
- `already shipped by skill '${input.catalogEntry.id}'.`);
180
- }
181
- return undefined;
182
- };
183
- const describeReference = (reference, fileRoot, projectDirectory) => 'repo_file' in reference
184
- ? { declaredPath: reference.repo_file, root: projectDirectory, kind: 'repo_file', rootLabel: 'project' }
185
- : { declaredPath: reference.file, root: fileRoot, kind: 'file', rootLabel: 'catalog' };
186
- const targetError = (skillPath, message) => ({
187
- severity: 'error',
188
- path: skillPath,
189
- message,
190
- });
191
- /**
192
- * Resolves one reference entry to its materializations: one for a literal
193
- * target, one per match for a glob target, and none for an omitted missing
194
- * `repo_file` target.
195
- */
196
- const resolveReferenceMaterializations = (input) => {
197
- const { declaredPath, root, kind, rootLabel } = describeReference(input.reference, input.fileRoot, input.projectDirectory);
198
- if (root === undefined) {
199
- return [
200
- targetError(input.skillPath, `Reference ${kind} '${declaredPath}' has no ${rootLabel} root to resolve from.`),
201
- ];
202
- }
203
- const context = { root, kind, rootLabel, section: input.section, skillPath: input.skillPath };
204
- if (isGlobPattern(declaredPath)) {
205
- return expandGlobMaterializations(declaredPath, context);
206
- }
207
- const resolved = resolveTargetMaterialization(declaredPath, context);
208
- return resolved === undefined ? [] : [resolved];
209
- };
210
- /**
211
- * Matches when a target uses glob syntax instead of naming one literal path.
212
- * Only the gitignore-style metacharacters count; braces are literal path
213
- * characters, so a braced filename resolves as a literal target.
214
- */
215
- const isGlobPattern = (declaredPath) => /[*?[]/u.test(declaredPath);
216
- /**
217
- * Expands a glob target against its root. Each match then follows the same
218
- * rules as a literal target, so a matched directory materializes recursively
219
- * while an escaping or special match fails validation individually.
220
- */
221
- const expandGlobMaterializations = (pattern, context) => {
222
- // Braces are literal path characters in this contract, but globSync
223
- // brace-expands them textually and offers no escape, so mixing them with
224
- // glob syntax fails validation rather than silently matching other paths.
225
- if (/[{}]/u.test(pattern)) {
226
- return [
227
- targetError(context.skillPath, `Reference ${context.kind} glob '${pattern}' must not contain braces; ` +
228
- `braces are literal path characters, not glob syntax.`),
229
- ];
230
- }
231
- // Sorted so diagnostics and collisions are deterministic across platforms.
232
- const matches = globSync(pattern, { cwd: context.root }).sort();
233
- if (matches.length === 0) {
234
- // A consuming project may not contain repo_file matches; the reference is omitted.
235
- return context.kind === 'repo_file'
236
- ? []
237
- : [targetError(context.skillPath, `Reference file glob '${pattern}' matched no files under '${context.root}'.`)];
238
- }
239
- const resolved = [];
240
- for (const match of matches) {
241
- const outcome = resolveTargetMaterialization(match, context);
242
- if (outcome !== undefined) {
243
- resolved.push(outcome);
244
- }
245
- }
246
- return resolved;
247
- };
248
- /** Validates one concrete target path — a literal target or a single glob match. */
249
- const resolveTargetMaterialization = (targetPath, context) => {
250
- const { root, kind, rootLabel, skillPath } = context;
251
- const error = (message) => targetError(skillPath, message);
252
- const sourcePath = isAbsolute(targetPath) ? targetPath : join(root, targetPath);
253
- if (!existsSync(sourcePath)) {
254
- // A consuming project may not contain a repo_file target; the reference is omitted.
255
- return kind === 'repo_file' ? undefined : error(`Reference file '${targetPath}' was not found under '${root}'.`);
256
- }
257
- const stats = statSync(sourcePath);
258
- if (!stats.isFile() && !stats.isDirectory()) {
259
- return error(`Reference ${kind} '${targetPath}' must be a regular file or directory.`);
260
- }
261
- if (escapesRoot(sourcePath, root)) {
262
- return error(`Reference ${kind} '${targetPath}' resolves outside its ${rootLabel} root '${root}'.`);
263
- }
264
- const directoryIssue = describeDirectoryTargetIssue(stats, sourcePath, targetPath, context);
265
- if (directoryIssue !== undefined) {
266
- return error(directoryIssue);
267
- }
268
- return {
269
- section: context.section,
270
- sourcePath,
271
- destinationName: basename(targetPath),
272
- kind: stats.isDirectory() ? 'directory' : 'file',
273
- };
274
- };
275
- /** Describes the first escaping, cyclic, or special path inside a directory target, if any. */
276
- const describeDirectoryTargetIssue = (stats, sourcePath, targetPath, context) => {
277
- if (!stats.isDirectory()) {
278
- return undefined;
279
- }
280
- const issue = scanDirectoryTarget(sourcePath, context.root, [realpathSync(sourcePath)], new Set());
281
- if (issue === undefined) {
282
- return undefined;
283
- }
284
- const containedPath = relative(sourcePath, issue.path);
285
- const problemDescriptions = {
286
- escaping: `which resolves outside its ${context.rootLabel} root '${context.root}'`,
287
- cyclic: 'which creates a symlink cycle',
288
- special: 'which is not a regular file or directory',
289
- };
290
- return (`Reference ${context.kind} '${targetPath}' contains ` + `'${containedPath}', ${problemDescriptions[issue.problem]}.`);
291
- };
292
- /**
293
- * Finds the first path inside a directory target whose realpath escapes the
294
- * reference root, a symlink that cycles back into an ancestor directory, or an
295
- * entry that is neither a regular file nor a directory. Directory
296
- * materialization dereferences symlinks recursively, so an escaping link would
297
- * smuggle outside content into the generated skill, a cyclic link would never
298
- * finish copying, and a special file such as a FIFO could block the copy.
299
- */
300
- const scanDirectoryTarget = (directory, root,
301
- /** Realpaths of the directories on the current descent, for cycle detection. */
302
- ancestry,
303
- /** Realpaths of fully scanned directories, so shared subtrees scan once. */
304
- scanned) => {
305
- for (const entry of readdirSync(directory, { withFileTypes: true })) {
306
- const entryPath = join(directory, entry.name);
307
- if (escapesRoot(entryPath, root)) {
308
- return { path: entryPath, problem: 'escaping' };
309
- }
310
- // statSync follows symlinks, so in-root symlinked subdirectories are
311
- // scanned too and a link to a special file is rejected like the file itself.
312
- const entryStats = statSync(entryPath);
313
- if (!entryStats.isFile() && !entryStats.isDirectory()) {
314
- return { path: entryPath, problem: 'special' };
315
- }
316
- if (!entryStats.isDirectory()) {
317
- continue;
318
- }
319
- const resolvedEntryPath = realpathSync(entryPath);
320
- if (ancestry.includes(resolvedEntryPath)) {
321
- return { path: entryPath, problem: 'cyclic' };
322
- }
323
- if (!scanned.has(resolvedEntryPath)) {
324
- scanned.add(resolvedEntryPath);
325
- const issue = scanDirectoryTarget(entryPath, root, [...ancestry, resolvedEntryPath], scanned);
326
- if (issue !== undefined) {
327
- return issue;
328
- }
329
- }
330
- }
331
- return undefined;
332
- };
333
- /**
334
- * Copies a directory target while dereferencing symlinks, so the generated
335
- * skill has no links back into catalog or project checkouts. cpSync's
336
- * dereference option only follows the top-level source path, so nested entries
337
- * are walked explicitly; copyFileSync preserves each file's mode.
338
- */
339
- const copyDirectoryDereferenced = (source, destination) => {
340
- mkdirSync(destination, { recursive: true });
341
- for (const entry of readdirSync(source, { withFileTypes: true })) {
342
- const sourcePath = join(source, entry.name);
343
- const destinationPath = join(destination, entry.name);
344
- // statSync follows symlinks; scanDirectoryTarget already rejected cycles.
345
- if (statSync(sourcePath).isDirectory()) {
346
- copyDirectoryDereferenced(sourcePath, destinationPath);
347
- }
348
- else {
349
- copyFileSync(sourcePath, destinationPath);
350
- }
351
- }
352
- };
353
- /** Targets must remain within their root after following symlinks. */
354
- const escapesRoot = (sourcePath, root) => {
355
- let resolvedSource;
356
- let resolvedRoot;
357
- try {
358
- resolvedSource = realpathSync(sourcePath);
359
- resolvedRoot = realpathSync(root);
360
- }
361
- catch {
362
- /* v8 ignore next 2 -- existsSync guards the source; a vanished root is a race we treat as escaping. */
363
- return true;
364
- }
365
- const relativePath = relative(resolvedRoot, resolvedSource);
366
- // A bare '..' or a '../' prefix escapes; a sibling name like '..config' does not.
367
- return relativePath === '..' || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath);
368
- };
369
- //# sourceMappingURL=SkillResolution.js.map