@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
@@ -0,0 +1,160 @@
1
+ import { Key, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
2
+
3
+ // Outfitter runtime extension. Unlike the setup walkthrough extension
4
+ // (outfitter-extension.js), this file is loaded into the real profile pi session. Its only job is
5
+ // to restore Outfitter's original "no provider connected yet" sign-in prompt: when pi starts with
6
+ // no models available, it offers to connect one and delegates to pi's native /login command, which
7
+ // persists credentials in pi's own agent directory and selects a default model in-session.
8
+ //
9
+ // The setup pi stays deliberately model-free and MUST NOT open /login (OFTR-010.1.4); that is why
10
+ // the auto-login lives here instead. This file needs no stamped launch-time values, so the CLI
11
+ // loads it directly by absolute path via --extension.
12
+
13
+ export default function outfitterRuntime(pi) {
14
+ let loginSubmitted = false;
15
+
16
+ const submitSlashCommand = async (ctx, command) => {
17
+ if (ctx.mode !== 'tui') return false;
18
+ ctx.ui.setEditorText(command);
19
+ await ctx.ui.custom(
20
+ (tui, _theme, _keybindings, done) => {
21
+ setTimeout(() => {
22
+ tui.focusedComponent?.handleInput?.('\r');
23
+ done(true);
24
+ }, 25);
25
+ return { render: () => [], invalidate: () => undefined };
26
+ },
27
+ { overlay: true, overlayOptions: { nonCapturing: true, visible: () => false } },
28
+ );
29
+ return true;
30
+ };
31
+
32
+ const getAvailableModelCount = async (ctx) => {
33
+ if (ctx.modelRegistry === undefined || typeof ctx.modelRegistry.getAvailable !== 'function') {
34
+ return ctx.model === undefined ? 0 : 1;
35
+ }
36
+ try {
37
+ const available = await ctx.modelRegistry.getAvailable();
38
+ return Array.isArray(available) ? available.length : 0;
39
+ } catch {
40
+ return ctx.model === undefined ? 0 : 1;
41
+ }
42
+ };
43
+
44
+ const confirmModelProviderConnection = async (ctx) => {
45
+ if (typeof ctx.ui.custom !== 'function') return true;
46
+ const selected = await selectDescribedOption(
47
+ ctx,
48
+ [
49
+ 'Pi does not have a model provider connected yet.',
50
+ 'Connect one now so Outfitter can use Pi.',
51
+ 'Credentials stay inside Pi.',
52
+ ],
53
+ [
54
+ {
55
+ value: 'connect',
56
+ label: 'Connect a model provider',
57
+ description: 'Open Pi /login to sign in via OAuth or an API key.',
58
+ },
59
+ ],
60
+ 'connect',
61
+ );
62
+ return selected === 'connect';
63
+ };
64
+
65
+ const openLoginIfNoModels = async (ctx) => {
66
+ if (loginSubmitted || ctx.mode !== 'tui') return;
67
+ const availableModelCount = await getAvailableModelCount(ctx);
68
+ if (availableModelCount > 0) return;
69
+ if (!(await confirmModelProviderConnection(ctx))) return;
70
+ loginSubmitted = await submitSlashCommand(ctx, '/login');
71
+ };
72
+
73
+ pi.on('session_start', async (event, ctx) => {
74
+ if (ctx.mode !== 'tui') return;
75
+ if (event.reason === 'startup') await openLoginIfNoModels(ctx);
76
+ });
77
+ }
78
+
79
+ // Renders a described-option picker identical to the setup walkthrough's, so the runtime sign-in
80
+ // prompt matches the rest of the Outfitter UI. Falls back to a plain label list when pi does not
81
+ // support custom UI, then resolves the chosen label back to its item value.
82
+ const selectDescribedOption = (ctx, titleLines, items, initialValue) =>
83
+ ctx.ui.custom((tui, theme, _keybindings, done) => {
84
+ let selectedIndex = Math.max(
85
+ 0,
86
+ items.findIndex((item) => item.value === initialValue),
87
+ );
88
+ const labelWidth = Math.max(...items.map((item) => item.label.length));
89
+ let cachedWidth;
90
+ let cachedLines;
91
+
92
+ const finish = (value) => done(value);
93
+ const refresh = () => {
94
+ cachedWidth = undefined;
95
+ cachedLines = undefined;
96
+ tui.requestRender?.();
97
+ };
98
+ const move = (delta) => {
99
+ selectedIndex = Math.max(0, Math.min(items.length - 1, selectedIndex + delta));
100
+ refresh();
101
+ };
102
+ const render = (width) => {
103
+ const maxWidth = typeof width === 'number' && width > 0 ? width : 120;
104
+ if (cachedLines && cachedWidth === maxWidth) return cachedLines;
105
+ const lines = [];
106
+ const add = (line) => lines.push(visibleWidth(line) > maxWidth ? truncateToWidth(line, maxWidth) : line);
107
+ const addWrapped = (line, widthForWrap = maxWidth, prefix = '') => {
108
+ for (const wrappedLine of wrapTextWithAnsi(line, Math.max(1, widthForWrap))) add(prefix + wrappedLine);
109
+ };
110
+ const renderSelectedItem = (prefix, label, description) => {
111
+ const baseLine = prefix + label;
112
+ if (!description) {
113
+ add(baseLine);
114
+ return;
115
+ }
116
+ const inlineDescriptionWidth = maxWidth - visibleWidth(baseLine) - 2;
117
+ const descriptionText = theme.fg('muted', description);
118
+ if (inlineDescriptionWidth >= 30) {
119
+ const [firstLine = '', ...remainingLines] = wrapTextWithAnsi(descriptionText, inlineDescriptionWidth);
120
+ add(baseLine + ' ' + firstLine);
121
+ const continuationPrefix = ' '.repeat(Math.min(maxWidth, visibleWidth(baseLine) + 2));
122
+ for (const line of remainingLines) add(continuationPrefix + line);
123
+ return;
124
+ }
125
+ add(baseLine);
126
+ addWrapped(descriptionText, maxWidth - 2, ' ');
127
+ };
128
+
129
+ add(theme.fg('accent', '─'.repeat(maxWidth)));
130
+ titleLines.forEach((line, index) =>
131
+ addWrapped(index === 0 ? theme.fg('text', ' ' + line) : theme.fg('dim', ' ' + line)),
132
+ );
133
+ lines.push('');
134
+ items.forEach((item, index) => {
135
+ const selected = index === selectedIndex;
136
+ const prefix = selected ? theme.fg('accent', '→ ') : ' ';
137
+ const paddedLabel = item.label.padEnd(labelWidth);
138
+ const label = selected ? theme.fg('accent', paddedLabel) : paddedLabel;
139
+ renderSelectedItem(prefix, label, selected ? item.description : undefined);
140
+ });
141
+ lines.push('');
142
+ add(theme.fg('dim', '↑↓ navigate enter select escape/ctrl+c cancel'));
143
+ add(theme.fg('accent', '─'.repeat(maxWidth)));
144
+ cachedWidth = maxWidth;
145
+ cachedLines = lines;
146
+ return lines;
147
+ };
148
+
149
+ return {
150
+ outfitterOptions: items.map((item) => item.label),
151
+ render,
152
+ invalidate: refresh,
153
+ handleInput: (data) => {
154
+ if (matchesKey(data, Key.up)) move(-1);
155
+ else if (matchesKey(data, Key.down)) move(1);
156
+ else if (matchesKey(data, Key.enter)) finish(items[selectedIndex]?.value);
157
+ else if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl('c'))) finish(undefined);
158
+ },
159
+ };
160
+ });
@@ -1,6 +1,14 @@
1
- import type { AgentLaunchPlan } from './AgentAdapter.js';
1
+ import type { AgentLaunchPlan } from '../projection/Projection.js';
2
2
  export interface AgentProcessLauncher {
3
3
  launch(plan: AgentLaunchPlan): Promise<number>;
4
4
  }
5
5
  export declare const launchAgentProcess: (launcher: AgentProcessLauncher, launchPlan: AgentLaunchPlan, agentId: string) => Promise<number>;
6
6
  export declare const resolveAgentLaunchExecutable: (launchPlan: AgentLaunchPlan) => AgentLaunchPlan;
7
+ export declare const spawnLauncher: AgentProcessLauncher;
8
+ /**
9
+ * Launches a resolved plan through the given spawn boundary. The install-hint agentId is derived
10
+ * from the logical launch command ('pi' | 'claude') so a missing-CLI failure always names the
11
+ * harness actually being launched, regardless of how the harness was selected (flag, settings
12
+ * default, or built-in fallback).
13
+ */
14
+ export declare const launchThroughSpawn: (spawn: AgentProcessLauncher, plan: AgentLaunchPlan) => Promise<number>;
@@ -39,6 +39,25 @@ export const resolveAgentLaunchExecutable = (launchPlan) => {
39
39
  env: { PI_SKIP_VERSION_CHECK: '1', ...launchPlan.env },
40
40
  };
41
41
  };
42
+ /* v8 ignore start -- real process spawn is covered by end-to-end smoke usage, not unit tests. */
43
+ export const spawnLauncher = {
44
+ async launch(plan) {
45
+ const { default: spawn } = await import('cross-spawn');
46
+ return await new Promise((resolve, reject) => {
47
+ const child = spawn(plan.command, [...plan.args], { stdio: 'inherit', env: { ...process.env, ...plan.env } });
48
+ child.on('error', reject); // ENOENT surfaces as an actionable install message
49
+ child.on('close', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
50
+ });
51
+ },
52
+ };
53
+ /* v8 ignore stop */
54
+ /**
55
+ * Launches a resolved plan through the given spawn boundary. The install-hint agentId is derived
56
+ * from the logical launch command ('pi' | 'claude') so a missing-CLI failure always names the
57
+ * harness actually being launched, regardless of how the harness was selected (flag, settings
58
+ * default, or built-in fallback).
59
+ */
60
+ export const launchThroughSpawn = (spawn, plan) => launchAgentProcess(spawn, resolveAgentLaunchExecutable(plan), plan.command);
42
61
  const isCommandNotFoundError = (error) => error !== null && typeof error === 'object' && 'code' in error && error.code === 'ENOENT';
43
62
  const agentCliInstallHints = {
44
63
  pi: 'Install Pi with `npm install -g @earendil-works/pi-coding-agent` (see https://pi.dev).',
@@ -1 +1 @@
1
- {"version":3,"file":"AgentLaunch.js","sourceRoot":"","sources":["../../src/agents/AgentLaunch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AgentLaunch.js","sourceRoot":"","sources":["../../src/agents/AgentLaunch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** pi's persistent agent directory (`~/.pi/agent`), the durable home for credentials. */
2
+ export declare const resolvePiUserAgentDirectory: (homeDirectory: string) => string;
3
+ /** Seeds the projection root with the user's existing pi credentials so pi sees them at startup. */
4
+ export declare const seedPiCredentials: (projectionRoot: string, piUserAgentDirectory: string) => void;
5
+ /** Copies credentials created or changed during the pi session back to the durable agent dir. */
6
+ export declare const persistPiCredentials: (projectionRoot: string, piUserAgentDirectory: string) => void;
@@ -0,0 +1,33 @@
1
+ // Persists pi credential/provider state across runs. Outfitter launches pi with PI_CODING_AGENT_DIR
2
+ // pointed at an ephemeral, profile-scoped projection root (OFTR-006.3.1/006.4), which pi also uses
3
+ // as the home for auth.json/models.json (pi-coding-agent config.js: getAgentDir()). Left alone, a
4
+ // /login inside that session would be discarded when the projection root is deleted after the run,
5
+ // so the auto sign-in prompt could never converge. To keep provider credentials durable — matching
6
+ // standalone pi and pre-#165 Outfitter — we seed the projection root from pi's persistent agent dir
7
+ // before launch and copy any changes back afterward.
8
+ import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ // Files that carry pi credentials and provider/model definitions. Both live in getAgentDir() and
11
+ // must survive across the ephemeral projection root.
12
+ const persistentPiStateFiles = ['auth.json', 'models.json'];
13
+ /** pi's persistent agent directory (`~/.pi/agent`), the durable home for credentials. */
14
+ export const resolvePiUserAgentDirectory = (homeDirectory) => join(homeDirectory, '.pi', 'agent');
15
+ const copyIfPresent = (sourcePath, destinationPath) => {
16
+ if (!existsSync(sourcePath))
17
+ return;
18
+ mkdirSync(join(destinationPath, '..'), { recursive: true });
19
+ copyFileSync(sourcePath, destinationPath);
20
+ };
21
+ /** Seeds the projection root with the user's existing pi credentials so pi sees them at startup. */
22
+ export const seedPiCredentials = (projectionRoot, piUserAgentDirectory) => {
23
+ for (const file of persistentPiStateFiles) {
24
+ copyIfPresent(join(piUserAgentDirectory, file), join(projectionRoot, file));
25
+ }
26
+ };
27
+ /** Copies credentials created or changed during the pi session back to the durable agent dir. */
28
+ export const persistPiCredentials = (projectionRoot, piUserAgentDirectory) => {
29
+ for (const file of persistentPiStateFiles) {
30
+ copyIfPresent(join(projectionRoot, file), join(piUserAgentDirectory, file));
31
+ }
32
+ };
33
+ //# sourceMappingURL=PiCredentialPersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PiCredentialPersistence.js","sourceRoot":"","sources":["../../src/agents/PiCredentialPersistence.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,mGAAmG;AACnG,kGAAkG;AAClG,mGAAmG;AACnG,mGAAmG;AACnG,oGAAoG;AACpG,qDAAqD;AACrD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,iGAAiG;AACjG,qDAAqD;AACrD,MAAM,sBAAsB,GAAG,CAAC,WAAW,EAAE,aAAa,CAAU,CAAC;AAErE,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,aAAqB,EAAU,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAElH,MAAM,aAAa,GAAG,CAAC,UAAkB,EAAE,eAAuB,EAAQ,EAAE;IAC1E,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO;IACpC,SAAS,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,YAAY,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,oGAAoG;AACpG,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,cAAsB,EAAE,oBAA4B,EAAQ,EAAE;IAC9F,KAAK,MAAM,IAAI,IAAI,sBAAsB,EAAE,CAAC;QAC1C,aAAa,CAAC,IAAI,CAAC,oBAAoB,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC,CAAC;AAEF,iGAAiG;AACjG,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,cAAsB,EAAE,oBAA4B,EAAQ,EAAE;IACjG,KAAK,MAAM,IAAI,IAAI,sBAAsB,EAAE,CAAC;QAC1C,aAAa,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC,CAAC"}
@@ -1,31 +1,23 @@
1
1
  // Assembles the top-level Outfitter Commander program from command objects.
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { Command } from 'commander';
4
- import { createProfileCommands } from './commands/profile/Command.js';
5
- import { createRunCommand, executeRunCommand } from './commands/RunCommand.js';
4
+ import { createDumpCommand } from './commands/DumpCommand.js';
5
+ import { createListCommand } from './commands/ListCommand.js';
6
+ import { createRunAgentCommand } from './commands/RunAgentCommand.js';
6
7
  import { createSetupCommand } from './commands/SetupCommand.js';
7
- import { createSyncCommand } from './commands/SyncCommand.js';
8
- import { createWelcomeCommand } from './commands/WelcomeCommand.js';
8
+ import { createValidateCommand } from './commands/ValidateCommand.js';
9
9
  export const createDefaultCommands = () => [
10
- createRunCommand(),
11
- createSetupCommand({
12
- /* v8 ignore next 2 -- covered by end-to-end CLI smoke usage; unit tests inject this dependency. */
13
- async launchSetupSourceProfile(input) {
14
- await executeRunCommand({
15
- ...input,
16
- profileId: input.profileId,
17
- });
18
- },
19
- }),
20
- createSyncCommand(),
21
- createWelcomeCommand(),
22
- ...createProfileCommands(),
10
+ createRunAgentCommand(),
11
+ createSetupCommand(),
12
+ createListCommand(),
13
+ createValidateCommand(),
14
+ createDumpCommand(),
23
15
  ];
24
16
  export const createOutfitterProgram = (commands = createDefaultCommands()) => {
25
17
  const program = new Command();
26
18
  program
27
19
  .name('outfitter')
28
- .description('Profile-oriented wrapper for launching pi, Claude Code, and future agent CLIs.')
20
+ .description('Resolve, compose, and launch .agents agents in pi, Claude Code, and future harnesses.')
29
21
  .version(readPackageVersion());
30
22
  for (const command of commands) {
31
23
  command.register(program);
@@ -1 +1 @@
1
- {"version":3,"file":"OutfitterCli.js","sourceRoot":"","sources":["../../src/cli/OutfitterCli.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAEpE,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAoB,EAAE,CAAC;IAC1D,gBAAgB,EAAE;IAClB,kBAAkB,CAAC;QACjB,mGAAmG;QACnG,KAAK,CAAC,wBAAwB,CAAC,KAAK;YAClC,MAAM,iBAAiB,CAAC;gBACtB,GAAG,KAAK;gBACR,SAAS,EAAE,KAAK,CAAC,SAAS;aAC3B,CAAC,CAAC;QACL,CAAC;KACF,CAAC;IACF,iBAAiB,EAAE;IACnB,oBAAoB,EAAE;IACtB,GAAG,qBAAqB,EAAE;CAC3B,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,WAAqC,qBAAqB,EAAE,EAAW,EAAE;IAC9G,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,WAAW,CAAC;SACjB,WAAW,CAAC,gFAAgF,CAAC;SAC7F,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAEjC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,GAAW,EAAE;IACtC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAwB,CAAC;IAE7F,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC,CAAC"}
1
+ {"version":3,"file":"OutfitterCli.js","sourceRoot":"","sources":["../../src/cli/OutfitterCli.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAoB,EAAE,CAAC;IAC1D,qBAAqB,EAAE;IACvB,kBAAkB,EAAE;IACpB,iBAAiB,EAAE;IACnB,qBAAqB,EAAE;IACvB,iBAAiB,EAAE;CACpB,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,WAAqC,qBAAqB,EAAE,EAAW,EAAE;IAC9G,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,WAAW,CAAC;SACjB,WAAW,CAAC,uFAAuF,CAAC;SACpG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAEjC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,GAAW,EAAE;IACtC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAwB,CAAC;IAE7F,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC,CAAC"}
@@ -4,8 +4,3 @@ export interface CommandObject {
4
4
  readonly description: string;
5
5
  register(program: Command): void;
6
6
  }
7
- export interface CommandDescriptor {
8
- readonly name: string;
9
- readonly description: string;
10
- }
11
- export declare const describeCommandObject: (command: CommandObject) => CommandDescriptor;
@@ -1,5 +1,2 @@
1
- export const describeCommandObject = (command) => ({
2
- name: command.name,
3
- description: command.description,
4
- });
1
+ export {};
5
2
  //# sourceMappingURL=CommandObject.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CommandObject.js","sourceRoot":"","sources":["../../../src/cli/commands/CommandObject.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAsB,EAAqB,EAAE,CAAC,CAAC;IACnF,IAAI,EAAE,OAAO,CAAC,IAAI;IAClB,WAAW,EAAE,OAAO,CAAC,WAAW;CACjC,CAAC,CAAC"}
1
+ {"version":3,"file":"CommandObject.js","sourceRoot":"","sources":["../../../src/cli/commands/CommandObject.ts"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ import type { CommandObject } from './CommandObject.js';
2
+ export interface DumpInput {
3
+ readonly homeDirectory: string;
4
+ readonly projectDirectory: string;
5
+ readonly agent?: string;
6
+ readonly out: string;
7
+ }
8
+ export interface DumpCommandResult {
9
+ readonly writtenPaths: readonly string[];
10
+ readonly messages: readonly string[];
11
+ readonly ok: boolean;
12
+ }
13
+ export interface DumpCommandDependencies {
14
+ readonly homeDirectory?: string;
15
+ readonly projectDirectory?: string;
16
+ readonly writeLine?: (message: string) => void;
17
+ }
18
+ export declare const executeDumpCommand: (input: DumpInput) => DumpCommandResult;
19
+ export declare const createDumpCommand: (dependencies?: DumpCommandDependencies) => CommandObject;
@@ -0,0 +1,54 @@
1
+ // Provides `outfitter dump --agent <id> --out <dir>` over the effective resource set.
2
+ import { Command } from 'commander';
3
+ import { dumpAgent } from '../../dump/Dump.js';
4
+ import { resolveEffectiveSet } from '../../resolver/ResolverContext.js';
5
+ import { resolveHomeDirectory, resolveProjectDirectory } from './ProcessDefaults.js';
6
+ const resolveAgentSlug = (settings, requested) => {
7
+ if (requested !== undefined) {
8
+ return requested;
9
+ }
10
+ if (settings.defaultAgent === undefined) {
11
+ throw new Error("No agent selected and no 'default_agent' in settings. Pass --agent <id>.");
12
+ }
13
+ return settings.defaultAgent;
14
+ };
15
+ export const executeDumpCommand = (input) => {
16
+ const { set, settings, settingsIssues } = resolveEffectiveSet(input);
17
+ if (settingsIssues.length > 0) {
18
+ throw new Error(`Cannot dump with invalid settings: ${settingsIssues.map((issue) => issue.message).join('; ')}`);
19
+ }
20
+ const agentSlug = resolveAgentSlug(settings, input.agent);
21
+ const result = dumpAgent(set, agentSlug, input.out);
22
+ const ok = result.errors.length === 0;
23
+ const messages = ok
24
+ ? [`Dumped '${agentSlug}' to ${input.out} (${result.writtenPaths.length} files).`, ...result.warnings]
25
+ : [...result.errors, ...result.warnings];
26
+ return { writtenPaths: result.writtenPaths, messages, ok };
27
+ };
28
+ export const createDumpCommand = (dependencies = {}) => ({
29
+ name: 'dump',
30
+ description: 'Write the composed .agents tree for an agent to a directory.',
31
+ register(program) {
32
+ program.addCommand(new Command('dump')
33
+ .description('Write the composed .agents tree for an agent to a directory.')
34
+ .option('--agent <id>', 'Agent slug to dump (default: settings default_agent).')
35
+ .option('--out <dir>', 'Output directory.', './outfitter-dump')
36
+ .action((options) => {
37
+ const result = executeDumpCommand({
38
+ /* v8 ignore next 2 -- process defaults are exercised by the CLI entrypoint, not unit tests. */
39
+ homeDirectory: resolveHomeDirectory(dependencies.homeDirectory),
40
+ projectDirectory: resolveProjectDirectory(dependencies.projectDirectory),
41
+ agent: options.agent,
42
+ out: options.out,
43
+ });
44
+ for (const message of result.messages) {
45
+ /* v8 ignore next -- console fallback is direct CLI behavior; tests inject a writer. */
46
+ (dependencies.writeLine ?? console.log)(message);
47
+ }
48
+ if (!result.ok) {
49
+ process.exitCode = 1;
50
+ }
51
+ }));
52
+ },
53
+ });
54
+ //# sourceMappingURL=DumpCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DumpCommand.js","sourceRoot":"","sources":["../../../src/cli/commands/DumpCommand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { CommandObject } from './CommandObject.js';
2
+ export interface ListInput {
3
+ readonly homeDirectory: string;
4
+ readonly projectDirectory: string;
5
+ readonly kind?: string;
6
+ readonly agent?: string;
7
+ }
8
+ export interface ListResult {
9
+ readonly messages: readonly string[];
10
+ }
11
+ export interface ListCommandDependencies {
12
+ readonly homeDirectory?: string;
13
+ readonly projectDirectory?: string;
14
+ readonly writeLine?: (message: string) => void;
15
+ }
16
+ export declare const executeListCommand: (input: ListInput) => ListResult;
17
+ export declare const createListCommand: (dependencies?: ListCommandDependencies) => CommandObject;
@@ -0,0 +1,77 @@
1
+ // Provides `outfitter list [kind]` over the effective resource set.
2
+ import { Command } from 'commander';
3
+ import { agentLocalKinds, compareSlugs, findResource, listAgentResources, listResources, resourceKinds, } from '../../resolver/Resource.js';
4
+ import { resolveEffectiveSet } from '../../resolver/ResolverContext.js';
5
+ import { resolveHomeDirectory, resolveProjectDirectory } from './ProcessDefaults.js';
6
+ const kindByPlural = new Map([
7
+ ['agents', 'agent'],
8
+ ['skills', 'skill'],
9
+ ['knowledge', 'knowledge'],
10
+ ['commands', 'command'],
11
+ ]);
12
+ const pluralByKind = new Map([
13
+ ['agent', 'agents'],
14
+ ['skill', 'skills'],
15
+ ['knowledge', 'knowledge'],
16
+ ['command', 'commands'],
17
+ ]);
18
+ const resolveKindFilter = (kind) => {
19
+ if (kind === undefined) {
20
+ return resourceKinds;
21
+ }
22
+ const resolved = kindByPlural.get(kind);
23
+ if (resolved === undefined) {
24
+ throw new Error(`Unknown resource kind '${kind}'. Expected one of: ${[...kindByPlural.keys()].join(', ')}.`);
25
+ }
26
+ return [resolved];
27
+ };
28
+ export const executeListCommand = (input) => {
29
+ const { set, settingsIssues } = resolveEffectiveSet(input);
30
+ if (settingsIssues.length > 0) {
31
+ const detail = settingsIssues.map((issue) => `${issue.filePath}#${issue.path} ${issue.message}`).join('; ');
32
+ throw new Error(`Cannot list resources with invalid settings: ${detail}`);
33
+ }
34
+ const messages = [];
35
+ if (input.agent !== undefined && findResource(set, 'agent', input.agent) === undefined) {
36
+ throw new Error(`Unknown agent '${input.agent}'. Run 'outfitter list agents' to see resolvable agents.`);
37
+ }
38
+ for (const kind of resolveKindFilter(input.kind)) {
39
+ const hasAgentContext = input.agent !== undefined && agentLocalKinds.includes(kind);
40
+ const globalResources = listResources(set, kind);
41
+ const localResources = hasAgentContext ? listAgentResources(set, input.agent, kind) : [];
42
+ const resources = new Map(globalResources.map((resource) => [resource.slug, resource]));
43
+ for (const resource of localResources)
44
+ resources.set(resource.slug, resource);
45
+ messages.push(`${pluralByKind.get(kind)}${hasAgentContext ? ` (agent ${input.agent})` : ''}:`);
46
+ messages.push(...(resources.size === 0
47
+ ? [' (none)']
48
+ : [...resources.values()]
49
+ .sort((left, right) => compareSlugs(left.slug, right.slug))
50
+ .map((resource) => ` ${resource.slug} [${resource.winner.layer.label}${resource.winner.ownerAgent === undefined ? '' : '; agent-local'}]`)));
51
+ }
52
+ return { messages };
53
+ };
54
+ export const createListCommand = (dependencies = {}) => ({
55
+ name: 'list',
56
+ description: 'List resolvable resources (agents, skills, knowledge, commands).',
57
+ register(program) {
58
+ program.addCommand(new Command('list')
59
+ .description('List resolvable resources (agents, skills, knowledge, commands).')
60
+ .argument('[kind]', 'Restrict to one kind: agents, skills, knowledge, or commands.')
61
+ .option('--agent <id>', 'Resolve resources in an agent context, including its agent-local skills/knowledge/commands.')
62
+ .action((kind, options) => {
63
+ const result = executeListCommand({
64
+ /* v8 ignore next 2 -- process defaults are exercised by the CLI entrypoint, not unit tests. */
65
+ homeDirectory: resolveHomeDirectory(dependencies.homeDirectory),
66
+ projectDirectory: resolveProjectDirectory(dependencies.projectDirectory),
67
+ kind,
68
+ agent: options.agent,
69
+ });
70
+ for (const message of result.messages) {
71
+ /* v8 ignore next -- console fallback is direct CLI behavior; tests inject a writer. */
72
+ (dependencies.writeLine ?? console.log)(message);
73
+ }
74
+ }));
75
+ },
76
+ });
77
+ //# sourceMappingURL=ListCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListCommand.js","sourceRoot":"","sources":["../../../src/cli/commands/ListCommand.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EACL,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,aAAa,GACd,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAExE,OAAO,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAmBrF,MAAM,YAAY,GAAsC,IAAI,GAAG,CAAC;IAC9D,CAAC,QAAQ,EAAE,OAAO,CAAC;IACnB,CAAC,QAAQ,EAAE,OAAO,CAAC;IACnB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,UAAU,EAAE,SAAS,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,YAAY,GAAsC,IAAI,GAAG,CAAC;IAC9D,CAAC,OAAO,EAAE,QAAQ,CAAC;IACnB,CAAC,OAAO,EAAE,QAAQ,CAAC;IACnB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,SAAS,EAAE,UAAU,CAAC;CACxB,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,CAAC,IAAwB,EAA2B,EAAE;IAC9E,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAExC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,uBAAuB,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/G,CAAC;IAED,OAAO,CAAC,QAAQ,CAAC,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAgB,EAAc,EAAE;IACjE,MAAM,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAE3D,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5G,MAAM,IAAI,KAAK,CAAC,gDAAgD,MAAM,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;QACvF,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,KAAK,0DAA0D,CAAC,CAAC;IAC3G,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpF,MAAM,eAAe,GAAG,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,cAAc,GAAG,eAAe,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QACxF,KAAK,MAAM,QAAQ,IAAI,cAAc;YAAE,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAE9E,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAE,GAAG,eAAe,CAAC,CAAC,CAAC,WAAW,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAChG,QAAQ,CAAC,IAAI,CACX,GAAG,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC;YACtB,CAAC,CAAC,CAAC,UAAU,CAAC;YACd,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC;iBACpB,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;iBAC1D,GAAG,CACF,CAAC,QAAQ,EAAE,EAAE,CACX,KAAK,QAAQ,CAAC,IAAI,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GACjD,QAAQ,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAClD,GAAG,CACN,CAAC,CACT,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,eAAwC,EAAE,EAAiB,EAAE,CAAC,CAAC;IAC/F,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,kEAAkE;IAC/E,QAAQ,CAAC,OAAgB;QACvB,OAAO,CAAC,UAAU,CAChB,IAAI,OAAO,CAAC,MAAM,CAAC;aAChB,WAAW,CAAC,kEAAkE,CAAC;aAC/E,QAAQ,CAAC,QAAQ,EAAE,+DAA+D,CAAC;aACnF,MAAM,CACL,cAAc,EACd,6FAA6F,CAC9F;aACA,MAAM,CAAC,CAAC,IAAwB,EAAE,OAA2B,EAAE,EAAE;YAChE,MAAM,MAAM,GAAG,kBAAkB,CAAC;gBAChC,+FAA+F;gBAC/F,aAAa,EAAE,oBAAoB,CAAC,YAAY,CAAC,aAAa,CAAC;gBAC/D,gBAAgB,EAAE,uBAAuB,CAAC,YAAY,CAAC,gBAAgB,CAAC;gBACxE,IAAI;gBACJ,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAC;YAEH,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACtC,uFAAuF;gBACvF,CAAC,YAAY,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;YACnD,CAAC;QACH,CAAC,CAAC,CACL,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { AgentLaunchPlan } from '../../projection/Projection.js';
2
+ export declare const isNonInteractivePiLaunch: (args: readonly string[]) => boolean;
3
+ /**
4
+ * Prepends `--extension <runtime>` to a pi launch so the auto sign-in prompt loads. The extension is
5
+ * a no-op whenever a model is already available, so it is safe to attach to every interactive pi
6
+ * run. Non-pi and non-interactive launches are returned unchanged.
7
+ */
8
+ export declare const attachPiRuntimeExtension: (plan: AgentLaunchPlan) => AgentLaunchPlan;
@@ -0,0 +1,47 @@
1
+ // Attaches the Outfitter runtime pi extension to interactive profile launches. The extension
2
+ // restores Outfitter's original "connect a model provider" sign-in prompt: when pi starts with no
3
+ // models available it offers to connect one and opens pi's native /login. Non-interactive pi
4
+ // launches (--print, --export, --mode json|print|rpc, …) are left untouched so scripted runs never
5
+ // prompt, and non-pi harnesses pass through unchanged.
6
+ import { existsSync } from 'node:fs';
7
+ import { fileURLToPath } from 'node:url';
8
+ const nonInteractivePiLaunchFlags = new Set(['--print', '-p', '--export', '--list-models']);
9
+ const nonInteractivePiModes = new Set(['json', 'print', 'rpc']);
10
+ const runtimeExtensionAsset = 'pi-extension/src/outfitter-runtime-extension.js';
11
+ export const isNonInteractivePiLaunch = (args) => args.some((arg, index) => {
12
+ if (nonInteractivePiLaunchFlags.has(arg))
13
+ return true;
14
+ if (arg === '--mode')
15
+ return nonInteractivePiModes.has(args[index + 1] ?? '');
16
+ if (arg.startsWith('--mode='))
17
+ return nonInteractivePiModes.has(arg.slice('--mode='.length));
18
+ return false;
19
+ });
20
+ // Resolves the runtime extension in either the repository layout (code/<asset>) or the packaged npm
21
+ // layout (code/cli/code/<asset>), mirroring SetupCommand's asset resolver.
22
+ const resolveRuntimeExtensionPath = () => {
23
+ const sourcePath = fileURLToPath(new URL(`../../../../${runtimeExtensionAsset}`, import.meta.url));
24
+ const packagePath = fileURLToPath(new URL(`../../../code/${runtimeExtensionAsset}`, import.meta.url));
25
+ /* v8 ignore else -- packaged layout is exercised by the npm package smoke test. */
26
+ if (existsSync(sourcePath))
27
+ return sourcePath;
28
+ /* v8 ignore next 2 -- packaged layout is covered by the package smoke test. */
29
+ if (existsSync(packagePath))
30
+ return packagePath;
31
+ return undefined;
32
+ };
33
+ /**
34
+ * Prepends `--extension <runtime>` to a pi launch so the auto sign-in prompt loads. The extension is
35
+ * a no-op whenever a model is already available, so it is safe to attach to every interactive pi
36
+ * run. Non-pi and non-interactive launches are returned unchanged.
37
+ */
38
+ export const attachPiRuntimeExtension = (plan) => {
39
+ if (plan.command !== 'pi' || isNonInteractivePiLaunch(plan.args))
40
+ return plan;
41
+ const extensionPath = resolveRuntimeExtensionPath();
42
+ /* v8 ignore next -- defensive: the runtime extension ships with the package, so it resolves in practice. */
43
+ if (extensionPath === undefined)
44
+ return plan;
45
+ return { ...plan, args: ['--extension', extensionPath, ...plan.args] };
46
+ };
47
+ //# sourceMappingURL=PiRuntimeLaunch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PiRuntimeLaunch.js","sourceRoot":"","sources":["../../../src/cli/commands/PiRuntimeLaunch.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,kGAAkG;AAClG,6FAA6F;AAC7F,mGAAmG;AACnG,uDAAuD;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAIzC,MAAM,2BAA2B,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;AAC5F,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;AAEhE,MAAM,qBAAqB,GAAG,iDAAiD,CAAC;AAEhF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,IAAuB,EAAW,EAAE,CAC3E,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;IACvB,IAAI,2BAA2B,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7F,OAAO,KAAK,CAAC;AACf,CAAC,CAAC,CAAC;AAEL,oGAAoG;AACpG,2EAA2E;AAC3E,MAAM,2BAA2B,GAAG,GAAuB,EAAE;IAC3D,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,eAAe,qBAAqB,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACnG,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,qBAAqB,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACtG,mFAAmF;IACnF,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IAC9C,+EAA+E;IAC/E,IAAI,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAC;IAChD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,IAAqB,EAAmB,EAAE;IACjF,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9E,MAAM,aAAa,GAAG,2BAA2B,EAAE,CAAC;IACpD,4GAA4G;IAC5G,IAAI,aAAa,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAE7C,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,aAAa,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACzE,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const resolveHomeDirectory: (provided?: string) => string;
2
+ export declare const resolveProjectDirectory: (provided?: string) => string;
@@ -0,0 +1,5 @@
1
+ // Resolves process-derived command defaults (home/project directories) in one tested place.
2
+ import { homedir } from 'node:os';
3
+ export const resolveHomeDirectory = (provided) => provided ?? homedir();
4
+ export const resolveProjectDirectory = (provided) => provided ?? process.cwd();
5
+ //# sourceMappingURL=ProcessDefaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessDefaults.js","sourceRoot":"","sources":["../../../src/cli/commands/ProcessDefaults.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAiB,EAAU,EAAE,CAAC,QAAQ,IAAI,OAAO,EAAE,CAAC;AAEzF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAAiB,EAAU,EAAE,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC"}