@ai-outfitter/outfitter 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE.md +20 -50
  2. package/README.md +36 -280
  3. package/code/enterprise/LICENSE +35 -0
  4. package/code/enterprise/README.md +5 -0
  5. package/dist/agents/AdapterProfileControls.d.ts +2 -2
  6. package/dist/agents/AdapterProfileControls.js +8 -1
  7. package/dist/agents/AdapterProfileControls.js.map +1 -1
  8. package/dist/agents/AgentAdapter.d.ts +10 -0
  9. package/dist/agents/AgentLaunch.d.ts +6 -0
  10. package/dist/agents/AgentLaunch.js +89 -0
  11. package/dist/agents/AgentLaunch.js.map +1 -0
  12. package/dist/agents/claude/ClaudeAdapter.js +18 -3
  13. package/dist/agents/claude/ClaudeAdapter.js.map +1 -1
  14. package/dist/agents/pi/PiAdapter.js +154 -14
  15. package/dist/agents/pi/PiAdapter.js.map +1 -1
  16. package/dist/cli/commands/PiLoginLaunch.d.ts +8 -2
  17. package/dist/cli/commands/PiLoginLaunch.js +726 -75
  18. package/dist/cli/commands/PiLoginLaunch.js.map +1 -1
  19. package/dist/cli/commands/RunCommand.d.ts +20 -3
  20. package/dist/cli/commands/RunCommand.js +102 -20
  21. package/dist/cli/commands/RunCommand.js.map +1 -1
  22. package/dist/cli/commands/SetupCommand.d.ts +11 -2
  23. package/dist/cli/commands/SetupCommand.js +266 -52
  24. package/dist/cli/commands/SetupCommand.js.map +1 -1
  25. package/dist/cli/commands/SyncCommand.d.ts +8 -1
  26. package/dist/cli/commands/SyncCommand.js +2 -1
  27. package/dist/cli/commands/SyncCommand.js.map +1 -1
  28. package/dist/cli/commands/WelcomeCommand.js +1 -1
  29. package/dist/cli/commands/WelcomeCommand.js.map +1 -1
  30. package/dist/cli/commands/assets/outfitter-ascii.txt +5 -0
  31. package/dist/cli/commands/profile/Command.d.ts +1 -0
  32. package/dist/cli/commands/profile/Command.js +3 -0
  33. package/dist/cli/commands/profile/Command.js.map +1 -1
  34. package/dist/cli/commands/profile/LintCommand.d.ts +19 -0
  35. package/dist/cli/commands/profile/LintCommand.js +123 -0
  36. package/dist/cli/commands/profile/LintCommand.js.map +1 -0
  37. package/dist/cli.js +8 -2
  38. package/dist/cli.js.map +1 -1
  39. package/dist/merge/ArrayMergePolicy.js.map +1 -1
  40. package/dist/merge/SettingsValueMerger.js.map +1 -1
  41. package/dist/profiles/Profile.d.ts +13 -1
  42. package/dist/profiles/Profile.js.map +1 -1
  43. package/dist/profiles/ProfileLoader.d.ts +4 -0
  44. package/dist/profiles/ProfileLoader.js +117 -17
  45. package/dist/profiles/ProfileLoader.js.map +1 -1
  46. package/dist/profiles/ProfileMerger.js +3 -0
  47. package/dist/profiles/ProfileMerger.js.map +1 -1
  48. package/dist/profiles/PromptIncludes.d.ts +32 -0
  49. package/dist/profiles/PromptIncludes.js +147 -0
  50. package/dist/profiles/PromptIncludes.js.map +1 -0
  51. package/dist/prompts/SystemPromptExport.d.ts +16 -0
  52. package/dist/prompts/SystemPromptExport.js +81 -0
  53. package/dist/prompts/SystemPromptExport.js.map +1 -0
  54. package/dist/schemas/profile.schema.json +37 -2
  55. package/dist/schemas/settings.schema.json +12 -0
  56. package/dist/settings/Settings.d.ts +5 -0
  57. package/dist/settings/Settings.js.map +1 -1
  58. package/dist/settings/SettingsLoader.js +3 -0
  59. package/dist/settings/SettingsLoader.js.map +1 -1
  60. package/dist/settings/SettingsMerger.js +8 -0
  61. package/dist/settings/SettingsMerger.js.map +1 -1
  62. package/package.json +8 -11
  63. package/src/schemas/profile.schema.json +37 -2
  64. package/src/schemas/settings.schema.json +12 -0
  65. package/doc/.deepreview +0 -30
  66. package/doc/architecture.md +0 -856
  67. package/doc/controllable-elements.md +0 -162
  68. package/doc/file_structure.md +0 -141
  69. package/doc/integration_test_system.md +0 -214
  70. package/doc/specs/validating_requirements_with_rules.md +0 -55
  71. package/doc/state_writeback_strategy.md +0 -342
  72. package/requirements/OFTR-001-project-foundation.md +0 -53
  73. package/requirements/OFTR-002-settings.md +0 -65
  74. package/requirements/OFTR-003-profiles.md +0 -60
  75. package/requirements/OFTR-004-sync-and-setup.md +0 -67
  76. package/requirements/OFTR-005-run-and-composite-profile.md +0 -60
  77. package/requirements/OFTR-006-agent-adapters.md +0 -66
  78. package/requirements/OFTR-007-controllable-elements.md +0 -32
  79. package/requirements/OFTR-008-requirements-governance.md +0 -42
  80. package/requirements/OFTR-009-release-publishing.md +0 -35
  81. package/requirements/OFTR-010-onboarding-welcome.md +0 -48
package/LICENSE.md CHANGED
@@ -1,58 +1,28 @@
1
- # Business Source License 1.1
1
+ Portions of this software are licensed as follows:
2
2
 
3
- ## Parameters
4
-
5
- **Licensor:** Unsupervised.com, Inc.
6
-
7
- **Licensed Work:** Outfitter
8
-
9
- The Licensed Work is © 2026 Unsupervised.com, Inc.
10
-
11
- **Additional Use Grant:** You may use the Licensed Work without a commercial license only when it is used with or for:
12
-
13
- - repositories that are publicly available under an open source license approved by the Open Source Initiative; or
14
- - repositories with fewer than 10 contributors.
15
-
16
- Use of the Licensed Work with or for a private repository with 10 or more contributors requires a commercial license from the Licensor.
17
-
18
- **Change Date:** 2030-01-14
19
-
20
- **Change License:** Apache License, Version 2.0
21
-
22
- ---
23
-
24
- ## License Terms
25
-
26
- The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work.
27
- The Licensor may make an Additional Use Grant, above, permitting limited production use.
28
-
29
- Effective on the Change Date, or the fourth anniversary of the first publicly available distribution of a specific version of the Licensed Work under this License, whichever comes first, the Licensor hereby grants you rights under the terms of the Change License, and the rights granted in the paragraph above terminate.
30
-
31
- If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must purchase a commercial license from the Licensor, its affiliated entities, or authorized resellers, or you must refrain from using the Licensed Work.
32
-
33
- All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License.
34
- This License applies separately for each version of the Licensed Work and the Change Date may vary for each version of the Licensed Work released by Licensor.
35
-
36
- You must conspicuously display this License on each original or modified copy of the Licensed Work.
37
- If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work.
38
-
39
- Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work.
40
-
41
- This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License).
42
-
43
- TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN "AS IS" BASIS.
44
- LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
3
+ - All content that resides under the `code/enterprise/` directory of this repository, if that directory exists, is licensed under the license defined in `code/enterprise/LICENSE`.
4
+ - Content outside of the above mentioned directory or restrictions above is available under the MIT license as defined below.
45
5
 
46
6
  ---
47
7
 
48
- ## Prohibited Uses
8
+ MIT License
49
9
 
50
- You may not use the Licensed Work with or for a private repository with 10 or more contributors unless you have purchased a commercial license from the Licensor, its affiliated entities, or authorized resellers.
10
+ Copyright (c) 2026 Unsupervised
51
11
 
52
- For purposes of this License, a repository's contributor count is the number of people who have made commits or other accepted contributions to that repository, or who otherwise have permission to contribute code to that repository, whichever is greater.
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
53
18
 
54
- **Clarification**: This prohibition does not prevent you from using the Licensed Work without a commercial license with or for:
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
55
21
 
56
- - repositories that are publicly available under an open source license approved by the Open Source Initiative, regardless of contributor count;
57
- - private repositories with fewer than 10 contributors;
58
- - educational or research contexts that satisfy one of the preceding repository-use allowances.
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
package/README.md CHANGED
@@ -1,295 +1,51 @@
1
- # outfitter
1
+ # Outfitter
2
2
 
3
- `outfitter` is intended to be a management wrapper for launching [`pi`](https://github.com/earendil-works/pi-coding-agent) and Claude Code with configurable, reusable profiles.
3
+ Outfitter builds effective agent profiles and launches them through wrapped agent CLIs like [`pi`](https://github.com/earendil-works/pi-coding-agent), Claude Code, and future adapters. Individuals, teams, and organizations can share and compose repeatable agent profiles.
4
4
 
5
- The goal is manageable agent CLI configuration:
5
+ If you have not tried [Pi](https://pi.dev) yet, Outfitter is the quickest path to a recommended Pi loadout for engineering work.
6
6
 
7
- - Organizations can define standard pi or claude loadouts, share them, and launch agent CLIs consistently across different environments.
8
- - Individuals can swap between configurations of their coding agent, share those, and easily migrate to new machines.
9
-
10
- If you haven't tried [Pi](https://pi.dev) yet — we think it's a great coding harness & Outfitter is an easy way to try it.
11
-
12
- - Install and run `outfitter` to load pi with our standard configuration for engineers.
13
-
14
- ## Install
15
-
16
- Install Outfitter globally from npm so the `outfitter` command is available on your PATH:
7
+ ## Quick start
17
8
 
18
9
  ```bash
19
10
  npm install -g @ai-outfitter/outfitter
20
- outfitter --help
21
- ```
22
-
23
- Upgrade with:
24
-
25
- ```bash
26
- npm update -g @ai-outfitter/outfitter
27
- ```
28
-
29
- Use `npx` when you want to test Outfitter without adding a global command:
30
-
31
- ```bash
32
- npx --yes @ai-outfitter/outfitter@latest --help
33
- ```
34
-
35
- For source development, see [`CONTRIBUTING.md`](./CONTRIBUTING.md).
36
-
37
- Outfitter launches agent CLIs but does not install them.
38
- Install the agents you plan to use separately:
39
-
40
- - [pi](https://github.com/earendil-works/pi-coding-agent) — follow its installation instructions; the `pi` command must be on your PATH.
41
- - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) — only needed if you launch with `--agent claude`.
42
-
43
- ## Why this exists
44
-
45
- Pi is highly configurable through settings directories, extensions, skills, prompts, themes, model settings, environment variables, and CLI flags.
46
- That flexibility is powerful, but businesses often need a higher-level control plane for repeatable deployments.
47
-
48
- `outfitter` should make it easy to answer questions like:
49
-
50
- - Which pi configuration should this employee or team use?
51
- - Which extensions, skills, prompts, models, and providers are approved?
52
- - Which credentials or environment variables should be present?
53
- - Should project-local `.pi` configuration be allowed, merged, or ignored?
54
- - How do we ship multiple standardized pi loadouts without manual setup?
55
-
56
- ## Intended use case
57
-
58
- Example profile concepts:
59
-
60
- - `engineering-default` — standard engineering loadout with approved tools, prompts, and models.
61
- - `support` — customer-support-focused prompt and restricted tool set.
62
- - `sandbox` — isolated experimentation profile with disposable config and sessions.
63
- - `regulated` — locked-down profile with stricter extensions, context, and session behavior.
64
-
65
- A launch flow is intended to look like:
66
-
67
- ```bash
68
- outfitter
69
- outfitter run --profile engineering-default
70
- outfitter run -p support -- --cwd ~/work/customer-issue
71
- outfitter sync
72
11
  outfitter setup
73
- outfitter setup https://github.com/my_account/outfitter_config
74
- outfitter welcome
75
- outfitter profile list
76
- outfitter profile create regulated --scope user
77
- ```
78
-
79
- Under the hood, `outfitter` translates a selected profile into the selected agent launch environment.
80
- Pi runs use `PI_CODING_AGENT_DIR`; Claude Code runs use `CLAUDE_CONFIG_DIR`; both receive supported CLI flags, prompts, model settings, and environment variables.
81
- Select the adapter with `outfitter run --agent <pi|claude>`, or set `default_agent` in `settings.yml`.
82
- If neither is set, Outfitter defaults to pi for backward compatibility.
83
- If `outfitter` is run before `outfitter setup`, it creates the initial settings and default profile automatically before launching.
84
- When that first-run setup has an interactive terminal, Outfitter continues into the same welcome onboarding used by `outfitter welcome`.
85
-
86
- `outfitter welcome` explains Outfitter and Pi, asks you to choose an initial built-in role, and recommends a Pi productivity loadout.
87
- The current built-in role choices are `engineer` and `data_analyst`; Outfitter creates the selected local profile on the fly.
88
- The recommended loadout includes `ulta-tasklist`, `deepwork`, `pi-subagents`, and `pi-mcp-adapter`; you can accept it, choose individual items, or skip loadout installation.
89
- If you skip loadout installation, the generated profile has no extensions or skills.
90
- If Pi does not appear to be logged in after welcome onboarding, Outfitter opens Pi with `/login` automatically; outside welcome onboarding it prints a `/login` reminder instead.
91
- Outfitter never collects or persists provider API keys itself.
92
-
93
- `settings.yml` can point at local profiles, full Git URIs, or GitHub shorthand sources with optional refs and repository subpaths:
94
-
95
- ```yaml
96
- remote_settings:
97
- - github: my_account/outfitter_config
98
- ref: main
99
- path: settings.yml
100
-
101
- profile_sources:
102
- - github: my_account/outfitter_config
103
- ref: main
104
- path: profiles
105
- ```
106
-
107
- Run `outfitter sync` to fetch/update remote settings and profiles before using them.
108
-
109
- By default, Outfitter keeps reusable runtime cache files under `~/.outfitter/cache`.
110
- Set `cache_directory` in `settings.yml` to choose a different cache root; relative values resolve from the settings file that declares them.
111
- The pi adapter symlinks composite profile `utilities/` and `bin/` paths into this cache so pi-managed utilities such as `fd` and `rg` survive across temporary composite profile directories.
112
-
113
- Settings can also define arbitrary nested `custom_settings` values for Outfitter-time composite profile templating:
114
-
115
- ```yaml
116
- custom_settings:
117
- build_commands:
118
- lint: npm run lint
119
- ```
120
-
121
- Generated composite profile files can reference them with Outfitter's LiquidJS-based custom delimiters:
122
-
123
- ```yaml
124
- command: '[[= outfitter.custom_settings.build_commands.lint ]]'
125
- ```
126
-
127
- Control tags use `[[% ... %]]`, for example `[[% for item in outfitter.custom_settings.items %]]`.
128
- Outfitter intentionally does not use common `{{ ... }}` delimiters, and plain shell expressions like `[[ -f package.json ]]` are left alone.
129
-
130
- ## Setup from a settings repository
131
-
132
- You can bootstrap a machine from a Git repository:
133
-
134
- ```bash
135
- outfitter setup https://github.com/my_account/outfitter_config
136
- ```
137
-
138
- `outfitter setup` requires an interactive terminal on both stdin and stdout.
139
- When a repository is provided, it clones or updates the repository in Outfitter's shared repository cache, then uses it as a non-overwriting starting point:
140
-
141
- - interactive setup-source onboarding shows the Outfitter welcome first, explains which source is being imported, asks whether to install profiles into user home or the current project, then asks one source-profile/default prompt;
142
- - if the user chooses home and `~/.outfitter/settings.yml` does not exist, Outfitter copies the starter `settings.yml`;
143
- - if the user chooses project and `<project>/.outfitter/settings.yml` does not exist, Outfitter copies the starter `settings.yml` and ensures `./profiles` is exposed;
144
- - if starter profiles exist, Outfitter copies missing profile files into the selected `profiles/` folder;
145
- - existing settings and profile files are otherwise left unchanged;
146
- - after setup, Outfitter runs the same sync behavior used by `outfitter sync`, then offers to start with the selected default profile or shows both `outfitter` and `outfitter --profile <profile>` start commands;
147
- - on initial interactive first-run setup, Outfitter skips the older default-profile prompt and lets welcome onboarding choose the generated local default profile;
148
- - outside that initial welcome handoff and outside setup-source import onboarding, Outfitter shows a short setup wizard that lists synced profiles and writes the selected default profile to user settings;
149
- - no-source interactive setup continues into welcome onboarding to record role and loadout choices.
150
-
151
- A setup repository can use either root-level Outfitter files:
152
-
153
- ```text
154
- outfitter_config/
155
- settings.yml
156
- profiles/
157
- engineering-default/
158
- profile.yml
159
- support/
160
- profile.yml
161
- ```
162
-
163
- or a `.outfitter/` layout:
164
-
165
- ```text
166
- outfitter_config/
167
- .outfitter/
168
- settings.yml
169
- profiles/
170
- engineering-default/
171
- profile.yml
172
- support/
173
- profile.yml
174
- ```
175
-
176
- Example `settings.yml` for a setup repository:
177
-
178
- ```yaml
179
- default_profile: engineering-default
180
-
181
- profile_sources:
182
- - path: ./profiles
183
-
184
- # Optional: keep loading future updates from this same repo.
185
- - github: my_account/outfitter_config
186
- ref: main
187
- path: profiles
188
- ```
189
-
190
- If you want ongoing centralized settings, use a small local `~/.outfitter/settings.yml` that points at remote settings:
191
-
192
- ```yaml
193
- remote_settings:
194
- - github: my_account/outfitter_config
195
- ref: main
196
- path: settings.yml
197
- ```
198
-
199
- Then run:
200
-
201
- ```bash
202
- outfitter sync
203
- ```
204
-
205
- ## Profile model sketch
206
-
207
- A profile will use YAML.
208
- An initial profile shape is:
209
-
210
- ```yaml
211
- id: engineering-default
212
- label: Engineering Default
213
- description: General software engineering profile for coding, tests, reviews, and repo navigation.
214
- inherits:
215
- - base-typescript
216
- - shared-prose
217
-
218
- controls:
219
- model: anthropic/claude-sonnet-4
220
- environment:
221
- TEAM_MODE: engineering
12
+ outfitter
222
13
  ```
223
14
 
224
- Set `template: true` on profiles such as `shared-prose` that should only be inherited by runnable profiles, not selected directly with `outfitter run`.
15
+ Outfitter launches agent CLIs; install the agents you plan to use separately.
225
16
 
226
- The exact stable schema is governed by the requirements in `requirements/` and the JSON Schema files in `src/schemas/`, which are still expected to evolve with implementation.
17
+ For the full walkthrough, see [Getting started](./docs/documentation/getting-started.md).
227
18
 
228
- Profiles can also ship DeepWork jobs for that profile under `deepwork/jobs/`.
229
- When Outfitter launches Pi, it adds contributing profile job folders to `DEEPWORK_ADDITIONAL_JOBS_FOLDERS` so the DeepWork frontend can discover profile-owned workflows without copying them into a project `.deepwork/jobs/` directory.
230
- By default, profiles with bundled jobs receive only their profile-bundled jobs; set `controls.pi.allow_external_deepwork_jobs: true` to also include inherited `DEEPWORK_ADDITIONAL_JOBS_FOLDERS` entries.
231
- Pi-specific job overrides remain supported under `cli_specific/pi/deepwork/jobs/`.
19
+ ## Profiles
232
20
 
233
- ### Updating profile-managed Pi extensions
234
-
235
- Profiles can request Pi extensions with `controls.pi.extensions`, for example:
21
+ [Profiles](./docs/documentation/profiles.md) compose the context, tools, prompts, skills, extensions, subagents, and DeepWork workflows that shape an agent. Profiles can be shared using [Profile Catalog Repos](./docs/documentation/profile-repository.md).
236
22
 
237
23
  ```yaml
24
+ # ~/.outfitter/profiles/home-default.yml
25
+ id: home-default
26
+ label: Home Default
27
+ description: Reusable personal defaults for Outfitter-managed Pi runs.
238
28
  controls:
239
- pi:
240
- extensions:
241
- - git:github.com/ai-outfitter/deepwork
242
- ```
243
-
244
- For normal users, extension updates should flow through the profile rather than
245
- through hand-edited Pi cache directories:
246
-
247
- 1. If your profile is remote-managed, run `outfitter sync` to fetch the latest
248
- Outfitter settings and profile sources.
249
- 2. Confirm the active profile names the desired extension source, such as
250
- `git:github.com/ai-outfitter/deepwork` after a repository move.
251
- 3. Start a new Outfitter-managed Pi session with `outfitter` or
252
- `outfitter run --profile <profile>`. Restart any already-running Pi session;
253
- newly registered tools and extension code are only loaded when Pi starts or
254
- reloads resources.
255
-
256
- `outfitter sync` updates Outfitter's own remote profile/settings cache. Pi still
257
- owns installation and loading of the `git:` extension declared by the resolved
258
- profile. Outfitter keeps Pi's `git/` and `tmp/` state paths persistent across
259
- runs so installed extensions are reusable, but users should not need one-off
260
- scripts that modify temporary extension cache paths.
261
-
262
- If an extension still appears stale after syncing profiles and restarting Pi,
263
- check the resolved profile first. A future Outfitter command may add a
264
- first-class extension-cache refresh path, but direct cache surgery is a
265
- troubleshooting fallback rather than the normal update flow.
266
-
267
- ## Design direction
268
-
269
- The current recommendation is to build `outfitter` around pi's existing native configuration mechanisms:
270
-
271
- 1. Use a temporary composite profile directory as `PI_CODING_AGENT_DIR` for each run.
272
- 2. Persist intentional pi state through adapter-declared symlinks to profile, native pi, or Outfitter cache files.
273
- Native pi fallback is only a durable target for declared state symlinks; it is not an inherited base profile layer.
274
- 3. Layer profile-controlled environment variables and pi CLI flags on top.
275
- 4. Use explicit `--extension` / `-e` injection for bootstrap behavior that needs to run inside pi.
276
- 5. Decide per profile whether project-local `.pi` overrides are allowed.
277
- 6. Keep the wrapper responsible for anything that must happen before pi starts, such as selecting config directories, setting credentials, or choosing session locations.
278
-
279
- ## Status
280
-
281
- This repository is under phased implementation.
282
-
283
- A minimal executable CLI exists, with initial settings/profile schemas, local and URI-backed profile loading, profile resolution internals, first-pass `setup`, `sync`, `profile list`, and `profile create` commands, and a first-pass `run` command for assembling a temporary composite profile and launching pi or Claude Code.
284
- Stable end-to-end pi launch behavior and user-facing examples will be hardened in a later phase.
285
- The initial dependency and architecture decisions are documented in `package.json`, `doc/architecture.md`, and `requirements/`.
286
-
287
- ## Future work
288
-
289
- - Define a stable profile schema.
290
- - Decide where organization-managed profiles are discovered from.
291
- - Harden stable `outfitter run --profile <profile>` behavior.
292
- - Add validation and inspection commands.
293
- - Expand user-facing documentation for resolved profile inheritance and composition.
294
- - Add locking / policy controls for business-managed environments.
295
- - Add examples for common organizational deployments.
29
+ provider: openai-codex
30
+ model: gpt-5.5
31
+ thinking: high
32
+ append_system_prompt:
33
+ - |
34
+ Use concise, evidence-backed engineering prose.
35
+ Prefer small, reviewable changes.
36
+ Keep durable decisions in repo files.
37
+ - repo_file: docs/architecture.md
38
+ ```
39
+
40
+ ## Repository map
41
+
42
+ | Path | Use it for |
43
+ | ----------------------------------------------- | -------------------------------------------------------- |
44
+ | [Documentation](./docs/documentation/README.md) | User-facing setup, profile, and profile-repository docs. |
45
+ | [Architecture](./docs/archtecture/README.md) | Architecture, runtime design, and internal conventions. |
46
+ | [Requirements](./docs/requirements/) | Formal OFTR requirements. |
47
+ | [CLI package](./code/cli/) | Published CLI package source, tests, skills, and config. |
48
+ | [Pi extension](./code/pi-extension/) | Future Pi extension package boundary. |
49
+ | [Contributing](./CONTRIBUTING.md) | Local development and release workflow. |
50
+ | [Changelog](./CHANGELOG.md) | Release history. |
51
+ | [License](./LICENSE.md) | License terms. |
@@ -0,0 +1,35 @@
1
+ The Unsupervised Enterprise license (the "Enterprise License")
2
+ Copyright (c) 2026 - present Unsupervised.com, Inc.
3
+
4
+ With regard to the Unsupervised Software:
5
+
6
+ This software and associated documentation files (the "Software") may only be
7
+ used in production, if you (and any entity that you represent) have agreed to,
8
+ and are in compliance with, the Unsupervised Subscription Terms of Service,
9
+ available via email (info@unsupervised.com) (the "Enterprise Terms"), or other
10
+ agreement governing the use of the Software, as agreed by you and Unsupervised,
11
+ and otherwise have a valid Unsupervised Enterprise license for the correct
12
+ number of user seats. Subject to the foregoing sentence, you are free to modify
13
+ this Software and publish patches to the Software. You agree that Unsupervised
14
+ and/or its licensors (as applicable) retain all right, title and interest in and
15
+ to all such modifications and/or patches, and all such modifications and/or
16
+ patches may only be used, copied, modified, displayed, distributed, or otherwise
17
+ exploited with a valid Unsupervised Enterprise license for the correct number of
18
+ user seats. Notwithstanding the foregoing, you may copy and modify the Software
19
+ for development and testing purposes, without requiring a subscription. You agree
20
+ that Unsupervised and/or its licensors (as applicable) retain all right, title
21
+ and interest in and to all such modifications. You are not granted any other
22
+ rights beyond what is expressly stated herein. Subject to the foregoing, it is
23
+ forbidden to copy, merge, publish, distribute, sublicense, and/or sell the
24
+ Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
28
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
29
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
30
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
+
33
+ For all third party components incorporated into the Unsupervised Software,
34
+ those components are licensed under the original license provided by the owner of
35
+ the applicable component.
@@ -0,0 +1,5 @@
1
+ # Outfitter Enterprise Code
2
+
3
+ Files in this directory and its descendants are licensed under the Outfitter Enterprise/Business Source License in [`LICENSE`](./LICENSE).
4
+
5
+ Everything outside `code/enterprise/**` is licensed under the root [`LICENSE.md`](../../LICENSE.md) terms.
@@ -1,4 +1,4 @@
1
- import type { ProfileControls } from '../profiles/Profile.js';
1
+ import type { AppendSystemPromptControl, ProfileControls } from '../profiles/Profile.js';
2
2
  export declare const genericControlNames: Set<string>;
3
3
  export declare const supportedControlNames: (controls: ReadonlySet<string>) => readonly string[];
4
4
  export declare const controlAliases: readonly [{
@@ -17,5 +17,5 @@ export declare const controlAliases: readonly [{
17
17
  export declare const mergeAgentSpecificControls: <T extends ProfileControls>(controls: ProfileControls, agentKey: "pi" | "claude") => T;
18
18
  export declare const flagValue: (flag: string, value: string | undefined) => readonly string[];
19
19
  export declare const repeatFlag: (flag: string, values: readonly string[] | undefined) => readonly string[];
20
- export declare const repeatFlagValue: (flag: string, value: string | readonly string[] | undefined) => readonly string[];
20
+ export declare const repeatFlagValue: (flag: string, value: AppendSystemPromptControl | undefined) => readonly string[];
21
21
  export declare const findUnsupportedControlNames: (controls: Readonly<Record<string, unknown>>, supportedControls: ReadonlySet<string>, knownControls?: ReadonlySet<string>) => string[];
@@ -15,6 +15,7 @@ export const genericControlNames = new Set([
15
15
  'system_prompt',
16
16
  'appendSystemPrompt',
17
17
  'append_system_prompt',
18
+ 'deepwork',
18
19
  'pi',
19
20
  'claude',
20
21
  ]);
@@ -42,7 +43,13 @@ export const repeatFlagValue = (flag, value) => {
42
43
  if (value === undefined) {
43
44
  return [];
44
45
  }
45
- return typeof value === 'string' ? [flag, value] : repeatFlag(flag, value);
46
+ if (typeof value === 'string') {
47
+ return [flag, value];
48
+ }
49
+ if (!Array.isArray(value)) {
50
+ return [];
51
+ }
52
+ return repeatFlag(flag, value.filter((entry) => typeof entry === 'string'));
46
53
  };
47
54
  export const findUnsupportedControlNames = (controls, supportedControls, knownControls = genericControlNames) => {
48
55
  const controlNames = new Set(Object.keys(controls));
@@ -1 +1 @@
1
- {"version":3,"file":"AdapterProfileControls.js","sourceRoot":"","sources":["../../src/agents/AdapterProfileControls.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AdapterProfileControls.js","sourceRoot":"","sources":["../../src/agents/AdapterProfileControls.ts"],"names":[],"mappings":"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"}
@@ -7,8 +7,17 @@ export interface AgentLaunchPlan {
7
7
  readonly args: readonly string[];
8
8
  readonly env: Readonly<Record<string, string>>;
9
9
  }
10
+ export interface AgentLaunchProfileLayer {
11
+ readonly profile: Profile;
12
+ readonly profilePath: string;
13
+ readonly sourceRootPath?: string;
14
+ readonly resourceRootPath?: string;
15
+ readonly layout?: 'directory' | 'flat-file';
16
+ }
10
17
  export interface AgentLaunchContext {
11
18
  readonly profileFolders?: readonly string[];
19
+ readonly profileLayers?: readonly AgentLaunchProfileLayer[];
20
+ readonly projectDirectory?: string;
12
21
  }
13
22
  export interface AgentCompositeProfilePlan {
14
23
  readonly compositeProfile: CompositeProfile;
@@ -22,6 +31,7 @@ export interface AgentAdapter {
22
31
  readonly rootDirectory: string;
23
32
  readonly profilePaths: readonly string[];
24
33
  readonly profileFolders?: readonly string[];
34
+ readonly profileLayers?: readonly AgentLaunchProfileLayer[];
25
35
  readonly homeDirectory?: string;
26
36
  readonly cacheDirectory?: string;
27
37
  readonly settings?: Settings;
@@ -0,0 +1,6 @@
1
+ import type { AgentLaunchPlan } from './AgentAdapter.js';
2
+ export interface AgentProcessLauncher {
3
+ launch(plan: AgentLaunchPlan): Promise<number>;
4
+ }
5
+ export declare const launchAgentProcess: (launcher: AgentProcessLauncher, launchPlan: AgentLaunchPlan, agentId: string) => Promise<number>;
6
+ export declare const resolveAgentLaunchExecutable: (launchPlan: AgentLaunchPlan) => AgentLaunchPlan;
@@ -0,0 +1,89 @@
1
+ // Turns a logical agent launch plan into an actual launched process: resolves the bundled pi
2
+ // binary, runs the launcher, and translates a missing agent CLI into actionable install guidance.
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ export const launchAgentProcess = async (launcher, launchPlan, agentId) => {
7
+ try {
8
+ return await launcher.launch(launchPlan);
9
+ }
10
+ catch (error) {
11
+ if (isCommandNotFoundError(error)) {
12
+ throw new Error(formatMissingAgentCliMessage(agentId, launchPlan.command), { cause: error });
13
+ }
14
+ throw error;
15
+ }
16
+ };
17
+ // Pi is bundled with Outfitter, so prefer the bundled binary launched through the current Node
18
+ // runtime. This avoids the `spawn pi ENOENT` first-run crash when pi is not on PATH. Other agents
19
+ // (e.g. claude) are still resolved from PATH and fall back to actionable install guidance. This is
20
+ // a launch-mechanism detail applied by the real spawn launcher; the reported launch plan stays
21
+ // logical (`pi <args>`).
22
+ export const resolveAgentLaunchExecutable = (launchPlan) => {
23
+ if (launchPlan.command !== 'pi') {
24
+ return launchPlan;
25
+ }
26
+ const bundledPiLaunch = resolveBundledPiLaunch();
27
+ /* v8 ignore next 3 -- defensive: pi is a bundled dependency, so resolution succeeds in practice. */
28
+ if (bundledPiLaunch === undefined) {
29
+ return launchPlan;
30
+ }
31
+ return {
32
+ ...launchPlan,
33
+ command: bundledPiLaunch.command,
34
+ args: [...bundledPiLaunch.prefixArgs, ...launchPlan.args],
35
+ };
36
+ };
37
+ const isCommandNotFoundError = (error) => error !== null && typeof error === 'object' && 'code' in error && error.code === 'ENOENT';
38
+ const agentCliInstallHints = {
39
+ pi: 'Install Pi with `npm install -g @earendil-works/pi-coding-agent` (see https://pi.dev).',
40
+ claude: 'Install Claude Code from https://claude.com/claude-code, then rerun with `--agent claude`.',
41
+ };
42
+ const formatMissingAgentCliMessage = (agentId, command) => {
43
+ const installHint = agentCliInstallHints[agentId];
44
+ const baseMessage = `Could not launch the '${agentId}' agent CLI: '${command}' is not installed or not on your PATH.`;
45
+ return installHint === undefined ? baseMessage : `${baseMessage} ${installHint}`;
46
+ };
47
+ const piPackageName = '@earendil-works/pi-coding-agent';
48
+ const resolveBundledPiLaunch = () => {
49
+ const binPath = resolveBundledPiBinPath();
50
+ /* v8 ignore next 3 -- defensive: pi is a bundled dependency, so its bin resolves in practice. */
51
+ if (binPath === undefined) {
52
+ return undefined;
53
+ }
54
+ return { command: process.execPath, prefixArgs: [binPath] };
55
+ };
56
+ // Resolve the pi bin from its bundled package. Any failure (pi missing, malformed manifest, bin
57
+ // file absent) throws and is caught so the caller falls back to a PATH lookup. Pi is ESM-only with
58
+ // a restricted `exports` map, so the package directory is located by resolving its main entry and
59
+ // walking up to the nearest package.json; its `bin.pi` then names the launchable script.
60
+ const resolveBundledPiBinPath = () => {
61
+ try {
62
+ const packageRoot = findPiPackageRoot(fileURLToPath(import.meta.resolve(piPackageName)));
63
+ const manifest = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf8'));
64
+ const binPath = join(packageRoot, manifest.bin.pi);
65
+ /* v8 ignore next 3 -- defensive: a resolved pi bin path exists on disk. */
66
+ if (!existsSync(binPath)) {
67
+ throw new Error(`Bundled pi bin '${binPath}' is missing.`);
68
+ }
69
+ return binPath;
70
+ }
71
+ catch {
72
+ /* v8 ignore next -- defensive: resolution falls back to a PATH lookup when pi cannot be located. */
73
+ return undefined;
74
+ }
75
+ };
76
+ // The resolved entry lives inside the pi package, so the nearest ancestor package.json is pi's own.
77
+ const findPiPackageRoot = (resolvedEntryPath) => {
78
+ let directory = dirname(resolvedEntryPath);
79
+ while (!existsSync(join(directory, 'package.json'))) {
80
+ const parentDirectory = dirname(directory);
81
+ /* v8 ignore next 3 -- defensive: a resolved entry always has an ancestor package.json. */
82
+ if (parentDirectory === directory) {
83
+ throw new Error('Could not locate the bundled pi package root.');
84
+ }
85
+ directory = parentDirectory;
86
+ }
87
+ return directory;
88
+ };
89
+ //# sourceMappingURL=AgentLaunch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentLaunch.js","sourceRoot":"","sources":["../../src/agents/AgentLaunch.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,kGAAkG;AAClG,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAQzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,QAA8B,EAC9B,UAA2B,EAC3B,OAAe,EACE,EAAE;IACnB,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/F,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,+FAA+F;AAC/F,kGAAkG;AAClG,mGAAmG;AACnG,+FAA+F;AAC/F,yBAAyB;AACzB,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,UAA2B,EAAmB,EAAE;IAC3F,IAAI,UAAU,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAChC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,eAAe,GAAG,sBAAsB,EAAE,CAAC;IAEjD,oGAAoG;IACpG,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO;QACL,GAAG,UAAU;QACb,OAAO,EAAE,eAAe,CAAC,OAAO;QAChC,IAAI,EAAE,CAAC,GAAG,eAAe,CAAC,UAAU,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC;KAC1D,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAAC,KAAc,EAAW,EAAE,CACzD,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAK,KAA4B,CAAC,IAAI,KAAK,QAAQ,CAAC;AAEpH,MAAM,oBAAoB,GAAqC;IAC7D,EAAE,EAAE,wFAAwF;IAC5F,MAAM,EAAE,4FAA4F;CACrG,CAAC;AAEF,MAAM,4BAA4B,GAAG,CAAC,OAAe,EAAE,OAAe,EAAU,EAAE;IAChF,MAAM,WAAW,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,yBAAyB,OAAO,iBAAiB,OAAO,yCAAyC,CAAC;IAEtH,OAAO,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,IAAI,WAAW,EAAE,CAAC;AACnF,CAAC,CAAC;AAOF,MAAM,aAAa,GAAG,iCAAiC,CAAC;AAExD,MAAM,sBAAsB,GAAG,GAAgC,EAAE;IAC/D,MAAM,OAAO,GAAG,uBAAuB,EAAE,CAAC;IAE1C,iGAAiG;IACjG,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;AAC9D,CAAC,CAAC;AAEF,gGAAgG;AAChG,mGAAmG;AACnG,kGAAkG;AAClG,yFAAyF;AACzF,MAAM,uBAAuB,GAAG,GAAuB,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,iBAAiB,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACzF,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAElF,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEnD,2EAA2E;QAC3E,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,eAAe,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,oGAAoG;QACpG,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC,CAAC;AAEF,oGAAoG;AACpG,MAAM,iBAAiB,GAAG,CAAC,iBAAyB,EAAU,EAAE;IAC9D,IAAI,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAE3C,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QACpD,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QAE3C,0FAA0F;QAC1F,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QAED,SAAS,GAAG,eAAe,CAAC;IAC9B,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC"}