@ai-outfitter/outfitter 1.5.0 → 1.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 (83) hide show
  1. package/README.md +11 -2
  2. package/code/pi-extension/src/outfitter-extension.js +9 -1
  3. package/dist/agents/AgentLaunch.d.ts +2 -2
  4. package/dist/agents/AgentLaunch.js +4 -3
  5. package/dist/agents/AgentLaunch.js.map +1 -1
  6. package/dist/agents/ClaudeStatePersistence.d.ts +18 -0
  7. package/dist/agents/ClaudeStatePersistence.js +225 -0
  8. package/dist/agents/ClaudeStatePersistence.js.map +1 -0
  9. package/dist/cli/OutfitterCli.js +1 -1
  10. package/dist/cli/OutfitterCli.js.map +1 -1
  11. package/dist/cli/commands/DumpCommand.d.ts +1 -0
  12. package/dist/cli/commands/DumpCommand.js +11 -1
  13. package/dist/cli/commands/DumpCommand.js.map +1 -1
  14. package/dist/cli/commands/ListCommand.d.ts +2 -0
  15. package/dist/cli/commands/ListCommand.js +15 -4
  16. package/dist/cli/commands/ListCommand.js.map +1 -1
  17. package/dist/cli/commands/RunAgentCommand.js +85 -17
  18. package/dist/cli/commands/RunAgentCommand.js.map +1 -1
  19. package/dist/cli/commands/SyncCommand.d.ts +8 -3
  20. package/dist/cli/commands/SyncCommand.js +126 -21
  21. package/dist/cli/commands/SyncCommand.js.map +1 -1
  22. package/dist/cli/commands/ValidateCommand.js +1 -1
  23. package/dist/cli/commands/ValidateCommand.js.map +1 -1
  24. package/dist/projection/CodexMcp.d.ts +11 -0
  25. package/dist/projection/CodexMcp.js +143 -0
  26. package/dist/projection/CodexMcp.js.map +1 -0
  27. package/dist/projection/Materialize.d.ts +3 -1
  28. package/dist/projection/Materialize.js +7 -3
  29. package/dist/projection/Materialize.js.map +1 -1
  30. package/dist/projection/ProjectHarness.js +66 -29
  31. package/dist/projection/ProjectHarness.js.map +1 -1
  32. package/dist/projection/Projection.d.ts +2 -0
  33. package/dist/resolver/AmbiguityWarnings.d.ts +7 -0
  34. package/dist/resolver/AmbiguityWarnings.js +86 -0
  35. package/dist/resolver/AmbiguityWarnings.js.map +1 -0
  36. package/dist/resolver/Layer.d.ts +8 -2
  37. package/dist/resolver/Layer.js +48 -8
  38. package/dist/resolver/Layer.js.map +1 -1
  39. package/dist/resolver/ResolverContext.d.ts +3 -1
  40. package/dist/resolver/ResolverContext.js +11 -2
  41. package/dist/resolver/ResolverContext.js.map +1 -1
  42. package/dist/resolver/ResolverValidation.d.ts +12 -1
  43. package/dist/resolver/ResolverValidation.js +30 -20
  44. package/dist/resolver/ResolverValidation.js.map +1 -1
  45. package/dist/schemas/settings.schema.json +1 -1
  46. package/dist/schemas/system-extension-hook.schema.json +38 -0
  47. package/dist/settings/Settings.d.ts +1 -1
  48. package/dist/settings/Settings.js +1 -1
  49. package/dist/settings/Settings.js.map +1 -1
  50. package/dist/settings/SettingsLoader.js.map +1 -1
  51. package/dist/setup/DefaultCatalog.d.ts +18 -0
  52. package/dist/setup/DefaultCatalog.js +74 -13
  53. package/dist/setup/DefaultCatalog.js.map +1 -1
  54. package/dist/sources/SourceCache.d.ts +20 -0
  55. package/dist/sources/SourceCache.js +31 -1
  56. package/dist/sources/SourceCache.js.map +1 -1
  57. package/dist/sources/TransitiveSources.d.ts +45 -0
  58. package/dist/sources/TransitiveSources.js +179 -0
  59. package/dist/sources/TransitiveSources.js.map +1 -0
  60. package/dist/system/SystemExtensionHook.d.ts +34 -0
  61. package/dist/system/SystemExtensionHook.js +190 -0
  62. package/dist/system/SystemExtensionHook.js.map +1 -0
  63. package/dist/validation/SchemaValidator.d.ts +1 -1
  64. package/dist/validation/SchemaValidator.js +2 -0
  65. package/dist/validation/SchemaValidator.js.map +1 -1
  66. package/docs/architecture/state_writeback_strategy.md +15 -3
  67. package/docs/documentation/README.md +14 -1
  68. package/docs/documentation/catalogs.md +61 -2
  69. package/docs/documentation/cli.md +2 -1
  70. package/docs/documentation/concepts.md +2 -2
  71. package/docs/documentation/hooks.md +33 -0
  72. package/docs/documentation/in-cluster.md +2 -0
  73. package/docs/documentation/migration.md +1 -1
  74. package/docs/documentation/personas.md +1 -1
  75. package/docs/documentation/settings.md +1 -1
  76. package/docs/documentation/state.md +27 -0
  77. package/docs/documentation/support-matrix.md +34 -23
  78. package/docs/documentation/usecases/org-onboarding-sdlc-report.md +143 -0
  79. package/docs/documentation/usecases/persona-reviews.md +1 -1
  80. package/docs/philosophy.md +20 -0
  81. package/package.json +1 -1
  82. package/src/schemas/settings.schema.json +1 -1
  83. package/src/schemas/system-extension-hook.schema.json +38 -0
@@ -2,6 +2,19 @@
2
2
 
3
3
  Outfitter lays out conventions for iterating on and sharing agent configuration — agent profiles, skills, and loadouts. The docs follow that arc: **set up** your own, **understand** the pieces, **share** them across projects and your org, **automate** them on more surfaces, then contribute back. Each page keeps to one concept and links onward when you need the next one, so nothing here has to be read up front.
4
4
 
5
+ Onboarding an organization? Start with
6
+ [Onboard an organization with an SDLC report](./usecases/org-onboarding-sdlc-report.md) —
7
+ one engineer runs the maturity assessment, then creates the org `.agents`
8
+ repository with the baseline report as its first commit.
9
+
10
+ ## Climb the ramp (runbooks)
11
+
12
+ One runbook per rung of [the adoption ramp](../philosophy.md#the-ramp-to-an-autonomous-lifecycle). Each starts where the previous one ended and closes with the one concrete step that begins the next rung. Their success checks are the signals an SDLC assessment reports, so "done" is something you run rather than something you judge.
13
+
14
+ - [Share one catalog](../runbooks/share-one-catalog.md) — one pinned catalog the organization shares, instead of per-laptop configuration. (→ delegated)
15
+ - [Run it without your laptop](../runbooks/run-without-your-laptop.md) — an event triggers the workflow, its output lands through review, and the session is captured. (→ automated)
16
+ - [Give the agent a residence](../runbooks/give-the-agent-a-residence.md) — a named, assignable agent with an account and somewhere to live. (→ governed)
17
+
5
18
  ## Start (you)
6
19
 
7
20
  - [Getting started](./getting-started.md) — install, first run, default agent.
@@ -36,7 +49,7 @@ The same composition runs on every surface; only the trigger changes.
36
49
  - [Container images](./containers.md) — run the published Debian-based image persistently, extend it with apt, or use the `-nix` variant.
37
50
  - [Recurring runs](./recurring-runs.md) — loops three ways: the local loop extension, Actions cron, cluster schedules.
38
51
  - [In-cluster agents](./in-cluster.md) — resident agents, CronJobs, and subagent Jobs via Link Operator.
39
- - [Hooks](./hooks.md) — harness hook wiring and the protocol gap.
52
+ - [Hooks](./hooks.md) — harness hook wiring, launcher-scope system observers, and the protocol gap.
40
53
  - [Dump](./dump-and-bake.md) — deterministic, self-contained `.agents/` dumps.
41
54
  - [State persistence](./state.md)
42
55
  - [Adapter support matrix](./support-matrix.md)
@@ -83,6 +83,43 @@ Remote entries additionally accept:
83
83
 
84
84
  Resources from all sources resolve by slug behind local layers, following [layer precedence](./concepts.md#layer-precedence). Agent-local skills keep their owning-agent namespace through cache and source merging. Outfitter reports shadowed IDs so consumers can see which source supplies a selected resource.
85
85
 
86
+ ### Catalog dependencies (transitive sources)
87
+
88
+ A catalog can depend on other catalogs by declaring `sources` in its own settings file
89
+ (`settings.yml` at its payload root, or `.agents/settings.yml`). Outfitter resolves those
90
+ declarations transitively: syncing and resolving a catalog also fetches and layers the catalogs it
91
+ declares, so one pinned root pulls in its dependency closure.
92
+
93
+ Transitive sources are deliberately the narrowest safe subset — a `github:` shorthand pinned to an
94
+ immutable ref — while the [remote-catalog trust model](https://github.com/ai-outfitter/outfitter/issues/212)
95
+ is defined. Anything else a catalog declares is skipped with a warning:
96
+
97
+ - **`github:` shorthand only.** A transitive source must be a `github: owner/repo` shorthand. A
98
+ `uri:` source declared by a catalog is skipped, because a URI can name an arbitrary git transport
99
+ (for example a local path or a remote helper) that a dependency should not be able to choose on
100
+ your behalf. Keeping to `github:` also routes every transitive fetch that `outfitter sync`
101
+ performs through the same private-catalog gate as your own sources. (The one exception is the
102
+ first-party default catalog's own closure, fetched during setup — see the note below.)
103
+ - **Whole repository only.** A transitive `github:` source may not carry a `path:` subpath, so a
104
+ declaration can never point outside the repository it fetches.
105
+ - **Pinned only.** A transitive source must pin an immutable `ref:` — a full commit SHA, or a
106
+ version tag such as `v1.2.0`. A commit SHA is truly immutable; a version tag is a pin the
107
+ dependency's maintainer could later move, in which case the next `outfitter sync` fetches the
108
+ new commit and reports it as `updated`. Pin dependencies you rely on to a SHA when you need the
109
+ closure to never change underneath you.
110
+ - **Content only.** A depended-on catalog contributes `.agents` payload resources. Nothing else in
111
+ its settings file — default agent, default harness, cache directory, `remote_settings` — takes
112
+ effect transitively.
113
+ - **Lower precedence.** Every source you configure directly outranks every transitive source;
114
+ deeper dependencies rank below shallower ones.
115
+ - Cycles and duplicates resolve once — the first occurrence wins and resolution terminates.
116
+
117
+ A fresh `outfitter` install fetches this closure during setup, so a default profile whose skills
118
+ live in a depended-on catalog works without a manual sync. Because the default catalog is the
119
+ first-party catalog Outfitter ships (pinned in the CLI), its bootstrap fetches the declared closure
120
+ directly; the interactive private-catalog prompt is a property of `outfitter sync`, which is where
121
+ you add your own third-party sources.
122
+
86
123
  ## Organization control repositories
87
124
 
88
125
  An `owner/.outfitter` repository distributes organization-wide resources plus shared settings that Outfitter layers below each user's local settings:
@@ -104,7 +141,10 @@ Remote settings are cached locally and merged at lower precedence than your proj
104
141
  1. Local settings are validated. Remote settings repositories are cloned or updated first, then
105
142
  merged settings are reloaded.
106
143
  2. Remote sources (including any added by remote settings) are cloned or updated.
107
- 3. Each synced source is validated; sync reports `updated`, `unchanged`, `skipped`, or `failed` per source.
144
+ 3. Sources declared by the synced catalogs themselves (see
145
+ [catalog dependencies](#catalog-dependencies-transitive-sources)) are fetched next, repeating
146
+ until the whole dependency closure is cached.
147
+ 4. Each synced source is validated; sync reports `updated`, `unchanged`, `skipped`, or `failed` per source.
108
148
 
109
149
  All repositories live under `<cache_directory>/repos/<encoded-uri-and-ref>/` (default
110
150
  `~/.agents/cache`). Pinned (`ref:`) sources stay on their selected ref until you change it; unpinned
@@ -119,6 +159,23 @@ synchronization; run sync explicitly when you want network updates.
119
159
 
120
160
  Private GitHub catalogs are an enterprise feature. When sync detects a private GitHub repository, it asks for confirmation before use and records the decision in your user settings. Review the Outfitter Enterprise license or your enterprise agreement before enabling private catalogs. Non-GitHub `uri:` sources use whatever git credentials your environment already has; credentials embedded in URIs are redacted from sync output.
121
161
 
162
+ ### How sync authenticates
163
+
164
+ Outfitter does not collect, store, or validate credentials. It delegates to `git`, so a private catalog clones with whatever credentials the surrounding environment already gives `git` — which differs by where sync runs:
165
+
166
+ | Where | Credential |
167
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
168
+ | Your machine | Your existing git configuration: SSH agent, credential helper, or `.netrc`. |
169
+ | GitHub Actions | The workflow token, configured for git — see [token-permissions.md](https://github.com/ai-outfitter/actions/blob/main/docs/token-permissions.md). |
170
+ | A cluster pod | Supplied by the deployment: `GIT_ASKPASS` over HTTPS, or `GIT_SSH_COMMAND` for a deploy key. |
171
+
172
+ Two failure modes are worth knowing before you hit them:
173
+
174
+ - **Credentials belong in the environment, not the URI.** Outfitter redacts credentials from a source URI before deriving its cache path, so a URI carrying a username produces a cache entry that later runs do not read. The source URI must be byte-identical everywhere it appears.
175
+ - **`outfitter run` does not sync.** A runtime that has never synced has an empty cache and cannot resolve a profile from it, however good its credentials are.
176
+
177
+ [The forge credential model](../architecture/forge-credential-model.md) covers which credential to use where, and why.
178
+
122
179
  ## Trust and review
123
180
 
124
181
  Adding a catalog source means trusting its authors with your agent runtime. A catalog's resources can shape prompts and policy (agents, `agents.md`, `system-prompt.md`), add MCP servers (`mcp.json`), and ship skills whose scripts execute on your machine.
@@ -129,6 +186,8 @@ Before adding a source, review it:
129
186
  2. Read every skill you will select, including its scripts and catalog-owned `file` references (see the [trust boundary](./skills.md#trust-boundary)).
130
187
  3. Review `mcp.json` — MCP servers are code with whatever access you grant them.
131
188
  4. Check `remote_settings` targets: a settings file can add further sources you did not review.
132
- 5. Confirm the repository's ownership and that its maintainers are who you expect.
189
+ 5. Check the catalog's own `sources`: its pinned `github:` dependencies are fetched transitively, so
190
+ review each one like the catalog itself.
191
+ 6. Confirm the repository's ownership and that its maintainers are who you expect.
133
192
 
134
193
  **Pin a `ref:`** — ideally a full commit SHA — for any catalog you do not maintain yourself, and always for catalogs consumed in CI (see [Running tasks in GitHub Actions](./actions.md)). A pinned ref makes updates an explicit, reviewable action — bump the ref after reviewing the diff — instead of silently pulling whatever the catalog publishes next.
@@ -14,13 +14,14 @@ Resolve, compose, and launch an agent. `run` is the default command, so plain `o
14
14
  | Argument / Option | Description |
15
15
  | --------------------- | -------------------------------------------------------------------------------- |
16
16
  | `[agent]` | Agent slug to run. Defaults to the settings `default_agent`. |
17
- | `--harness <harness>` | Harness to launch in: `pi` or `claude`. Defaults to `default_harness`. |
17
+ | `--harness <harness>` | Harness to launch in: `pi`, `claude`, or `codex`. Defaults to `default_harness`. |
18
18
  | `--strict` | Fail instead of warning when the adapter cannot project part of the composition. |
19
19
 
20
20
  Any other arguments and unrecognized options are passed through to the launched harness:
21
21
 
22
22
  ```bash
23
23
  outfitter run engineer --harness claude
24
+ outfitter run engineer --harness codex -- exec "review this repo"
24
25
  outfitter run persona-reviewer -- --print "summarize this repo"
25
26
  ```
26
27
 
@@ -12,7 +12,7 @@ flowchart LR
12
12
  adapter --> harness[Harness]
13
13
  ```
14
14
 
15
- Settings tell Outfitter where `.agents` resources come from; sources supply protocol resource trees; the resolver merges the layered trees into one effective resource set; the selected agent composes its loadout — skills, subagents, model, and so on — from that set by slug; and an adapter projects the composed agent into harness-specific files, flags, and environment variables before launching the harness (pi or Claude Code).
15
+ Settings tell Outfitter where `.agents` resources come from; sources supply protocol resource trees; the resolver merges the layered trees into one effective resource set; the selected agent composes its loadout — skills, subagents, model, and so on — from that set by slug; and an adapter projects the composed agent into harness-specific files, flags, and environment variables before launching the harness (pi, Claude Code, or Codex CLI).
16
16
 
17
17
  ## The `.agents` protocol
18
18
 
@@ -80,7 +80,7 @@ Listing, validation, running, and dumping all share one resolver. What `outfitte
80
80
 
81
81
  ## Adapters
82
82
 
83
- An adapter projects composed resources into one agent CLI's native configuration — files, command-line flags, and environment variables. Pi is the primary and most complete adapter; a Claude Code adapter is supported with gaps. When an adapter cannot honor part of a composition it warns to stderr, or fails when `--strict` is set. See the [adapter support matrix](./support-matrix.md).
83
+ An adapter projects composed resources into one agent CLI's native configuration — files, command-line flags, and environment variables. Pi is the primary and most complete adapter; Claude Code and Codex CLI adapters are supported with gaps. When an adapter cannot honor part of a composition it warns to stderr, or fails when `--strict` is set. See the [adapter support matrix](./support-matrix.md).
84
84
 
85
85
  ## State persistence
86
86
 
@@ -15,6 +15,39 @@ See the [Claude Code hooks documentation](https://code.claude.com/docs/en/hooks)
15
15
 
16
16
  Pi supports a bootstrap hook via its extension mechanism: an extension passed with `--extension` runs at session start and can register tools, providers, and runtime behavior. Outfitter's own onboarding flow uses this channel. For recurring per-event behavior, Pi extensions are the native surface.
17
17
 
18
+ ## System extension hooks
19
+
20
+ An organization can make a local Pi observer load by default on every `outfitter run` by installing a system extension hook. Outfitter reads hook documents in lexical filename order from:
21
+
22
+ 1. `$OUTFITTER_SYSTEM_DIR/*.yml` when the variable is set (the test/development seam), or
23
+ 2. `/etc/outfitter/system.d/*.yml` on Linux, or
24
+ 3. `/Library/Application Support/Outfitter/system.d/*.yml` on macOS.
25
+
26
+ Each file contributes extensions and environment additively; files do not override each other. Reusing the same environment key for the same harness in two files is an error rather than an implicit precedence rule. An absent directory is a no-op. An unreadable or malformed document, including one naming an extension path that does not exist, aborts the run whether or not `--strict` is set.
27
+
28
+ ```yaml
29
+ name: pensieve
30
+ harnesses:
31
+ pi:
32
+ extensions:
33
+ - /nix/store/example-pensieve/lib/pensieve/collectors/pi
34
+ env:
35
+ PENSIEVE_SINK: https://pensieve.example.com
36
+ PENSIEVE_INSTALL_SCOPE: launcher
37
+ ```
38
+
39
+ Extension entries must be absolute paths that already exist. Outfitter resolves source-directory, hook-document, and extension symlinks to physical paths before loading them; a dangling link is fatal, and `OUTFITTER_SYSTEM_HOOK_SOURCE` records the physical source directory. Package and Git specifiers are rejected, so a launch never installs a system extension from the network. Only `name` and `harnesses` are accepted at the document root, and each harness entry can contain only `extensions` and `env`; a system hook cannot select an agent, harness, model, tool, skill, or prompt.
40
+
41
+ For Pi, Outfitter prepends the configured `--extension <path>` arguments after projection on every launch, including `--mode rpc`, print, and other non-interactive launches. Hook documents cannot name the Outfitter-controlled `PI_CODING_AGENT_DIR` or `PI_CODING_AGENT_SESSION_DIR` variables. They also reject `NODE_OPTIONS`, `NODE_REPL_EXTERNAL_MODULE`, `OPENSSL_CONF`, `LD_PRELOAD`, `LD_AUDIT`, `LD_LIBRARY_PATH`, and every `DYLD_*` variable because those can change process loading before Pi starts. Other hook environment is below the launch plan's own environment but above the parent process environment at spawn. `harnesses.claude` and `harnesses.codex` documents validate but are ignored with a warning; Outfitter has no equivalent extension argument for those adapters. Their native managed configuration is the stronger policy surface.
42
+
43
+ ### Launcher scope, not managed scope
44
+
45
+ This mechanism changes who owns the file that names an extension. It does not change Pi's configuration resolution because Pi never reads the system hook document. The accurate guarantee is **collection is on by default and the organization owns the configuration**, not that the session cannot turn collection off.
46
+
47
+ The `--no-extensions` option does not disable explicitly passed `--extension` paths, so it does not bypass a system hook. A session can still execute Outfitter's bundled Pi binary directly, never going through the Outfitter launcher, or set `OUTFITTER_SYSTEM_DIR` to an empty directory. Every launch whose platform resolves a hook directory records that choice in `OUTFITTER_SYSTEM_HOOK_SOURCE`: the normal value is the resolved physical system path, while an override is stamped as `env-override:<resolved-physical-path>`. Downstream evidence can therefore distinguish the normal system source from the session-settable bypass.
48
+
49
+ The normal Linux and macOS directories are root-owned. Outfitter deliberately fails closed on their operator errors: a malformed file should fail on a canary boot, while failing open could silently produce fleet sessions without collection. Those sessions must be treated as unattested rather than clean.
50
+
18
51
  ## Roadmap
19
52
 
20
53
  > **TODO (protocol gap):** hooks are the one behavioral surface the pinned protocol revision does not model, which means hook definitions cannot yet be expressed portably in a `.agents` tree and projected per harness. The path `agents/<agent-id>/hooks/<hook-id>/` is reserved for a future agent-local hook entity and deliberately has no resolution or projection behavior today. Outfitter may need to ship its own hooks extension that adapters translate to Claude `settings.json` hooks and Pi extensions respectively, or drive the concept into a future protocol revision. Until one of those lands, treat hooks as harness-native configuration and keep them thin: call scripts that live in the tree rather than embedding logic in hook definitions.
@@ -11,6 +11,8 @@ The Link Operator runs Outfitter-composed agents inside a Kubernetes cluster. Th
11
11
 
12
12
  The operator never interprets a profile, reads a secret's contents, or invokes the model — it provisions and starts; the agent layer does the rest.
13
13
 
14
+ Because the operator does not look inside a Secret, the credentials a resident agent carries are the deployment's decision. A resident agent needs two forge tokens rather than one: the notification wake path accepts only a classic token, while its repository work should carry the narrowest credential available. [Give the agent a residence](../runbooks/give-the-agent-a-residence.md) is the procedure; [the forge credential model](../architecture/forge-credential-model.md) is the reasoning.
15
+
14
16
  ## Execution shapes
15
17
 
16
18
  | Shape | Kubernetes resource | Trigger |
@@ -21,7 +21,7 @@ This page is the manual migration reference, and the bundled Outfitter skill can
21
21
  | `<project>/.outfitter/local/settings.yml` (nested dir) | `<project>/.agents/settings.local.yml` (flat, gitignored) |
22
22
  | `profile_sources` | `sources` supplying `.agents` payloads ([catalogs](./catalogs.md)) |
23
23
  | `default_profile` | `default_agent` naming an agent slug |
24
- | `outfitter run --profile <file-based id>` | `outfitter run <agent-id>` (choose the harness with `--harness pi\|claude`) |
24
+ | `outfitter run --profile <file-based id>` | `outfitter run <agent-id>` (choose the harness with `--harness pi\|claude\|codex`) |
25
25
  | `outfitter profile list` / `create` / `lint` | `outfitter list agents` / author files directly / `outfitter validate` |
26
26
  | `profile_export` / `generated-system-prompt.md` | `outfitter dump` ([Dump and bake](./dump-and-bake.md)) |
27
27
  | Free-form CI prompt + profile in `ai-outfitter/actions` | An agent run with structured inputs ([Actions](./actions.md)); the task/bake surface is a [future RFC](./tasks.md) |
@@ -32,7 +32,7 @@ Neither tier is a resource Outfitter resolves; both are ordinary directories of
32
32
 
33
33
  ## Three ways to consume the same file
34
34
 
35
- - **Appended at launch**: `outfitter run persona-reviewer --append-prompt docs/personas/platform-lead.md -- …` — the direct run is the underlying interface, and the reviewer adopts the file as its identity for that session only. Pass `--append-prompt` rather than spelling the harness flag yourself after `--`: pi and Claude Code take append-prompt documents through different flags, so a hand-written passthrough only works on the harness it was written for, and fails silently on the other. An agent using the [`persona-review`](https://github.com/ai-outfitter/community-profiles/tree/main/skills/persona-review) skill can drive the same run in the background or synchronously and capture its report in a durable file. See [Persona reviews](./usecases/persona-reviews.md) for the runnable form of both.
35
+ - **Appended at launch**: `outfitter run persona-reviewer --append-prompt docs/personas/platform-lead.md -- …` — the direct run is the underlying interface, and the reviewer adopts the file as its identity for that session only. Pass `--append-prompt` rather than spelling the harness flag yourself after `--`: pi and Claude Code take append-prompt documents through different native flags. Codex has no native append flag yet, so its adapter warns that the document is dropped. An agent using the [`persona-review`](https://github.com/ai-outfitter/community-profiles/tree/main/skills/persona-review) skill can drive the same run in the background or synchronously and capture its report in a durable file. See [Persona reviews](./usecases/persona-reviews.md) for the runnable form of both supported identity projections.
36
36
  - **Pasted into a web agent**: upload or paste the file unchanged into claude.ai project knowledge or a ChatGPT project as stakeholder context. Same artifact, zero conversion.
37
37
  - **Ordinary reading context**: any agent doing product planning, research, or writing can read the file to know who the work is for.
38
38
 
@@ -22,7 +22,7 @@ In a standalone `.agents` repository the repository root is the tree, so the fil
22
22
  ```yaml
23
23
  # .agents/settings.yml
24
24
  default_agent: engineer # which agent runs by default
25
- default_harness: pi # which harness to launch: pi or claude
25
+ default_harness: pi # which harness to launch: pi, claude, or codex
26
26
 
27
27
  # Where protocol resources come from, beyond this tree and ~/.agents.
28
28
  sources:
@@ -190,6 +190,33 @@ The last form is how a resident or in-cluster agent keeps continuity across rest
190
190
 
191
191
  ## Claude Code state paths
192
192
 
193
+ Claude credentials need a narrow adapter bridge in addition to the path-keyed state below. Claude
194
+ reads `.credentials.json` and `.claude.json` directly from `CLAUDE_CONFIG_DIR`; the ephemeral
195
+ projection gives `.credentials.json` no durable home, and `.claude.json`'s native location
196
+ (`~/.claude.json`, outside `~/.claude`) does not share its config-dir-relative path. Outfitter
197
+ seeds `.credentials.json` before launch. It seeds `oauthAccount` and `hasCompletedOnboarding` when
198
+ those keys exist in durable state. It also seeds the current working directory's accepted-trust bit
199
+ only when that exact trust decision already exists in durable state. Afterward it copies back the
200
+ whole `.credentials.json` when changed and atomically merges `oauthAccount`. If the durable
201
+ credentials also changed after seeding, Outfitter preserves that concurrent refresh and warns
202
+ instead of copying the projected credentials back. Claude MCP OAuth tokens
203
+ live under `mcpOAuth` in `.credentials.json`, keyed by `<serverName>|<hash>`, so server
204
+ authorizations acquired in an Outfitter-launched Claude session persist across runs through that
205
+ whole-file copy-back. Outfitter never copies the full machine-local `~/.claude.json` into a
206
+ projection or merges its other projected state back. Trust accepted inside an Outfitter session is
207
+ therefore discarded, so Claude prompts for trust on every run in a workspace that was never trusted
208
+ natively.
209
+
210
+ Claude session history has a second narrow bridge because `CLAUDE_CONFIG_DIR` also redirects
211
+ Claude's native `projects/` tree into the temporary projection. Before launch, Outfitter derives
212
+ Claude's project slug from the absolute working directory and copies only that slug directory from
213
+ `~/.claude/projects/`. This keeps other projects' transcripts out of the projection while making
214
+ `claude --continue` and `claude --resume` see earlier native or Outfitter-launched sessions. After
215
+ the run exits or throws, Outfitter merges every new or content-changed regular session file from
216
+ the projection's slug directories back into `~/.claude/projects/` atomically with mode `0600`.
217
+ Durable files are never deleted. A seed or copy-back failure emits a warning and does not replace
218
+ Claude's exit code or error.
219
+
193
220
  The Claude Code adapter declares these paths:
194
221
 
195
222
  ```yaml
@@ -1,6 +1,6 @@
1
1
  # Adapter support matrix
2
2
 
3
- What Outfitter can project per agent CLI. Pi is the primary and most complete adapter; Claude Code is supported with gaps.
3
+ What Outfitter can project per agent CLI. Pi is the primary and most complete adapter; Claude Code and Codex CLI are supported with gaps.
4
4
 
5
5
  Status values:
6
6
 
@@ -12,31 +12,42 @@ When a composition requests something an adapter cannot project, Outfitter warns
12
12
 
13
13
  Tasks and bake are not in this matrix — they are the subject of a [separate upcoming RFC](./tasks.md).
14
14
 
15
- | What Outfitter projects | Pi | Claude Code |
16
- | ------------------------------------------------------------------------ | --------- | ----------- |
17
- | Agent config directory | Supported | Supported |
18
- | Session directory | Supported | Supported |
19
- | Agent identity (`system-prompt.md`, `agents.md`, `agents/<id>/agent.md`) | Supported | Supported |
20
- | Subagents (`agents/<id>` as harness delegates) | Supported | Supported |
21
- | Skills (`skills/<id>`) | Supported | Partial |
22
- | Commands (`commands/`) | Supported | Partial |
23
- | Knowledge (`knowledge/`) | Supported | Partial |
24
- | Model selection (`models.json`) | Supported | Partial |
25
- | MCP servers (`mcp.json`) | Supported | Supported |
26
- | Extensions (agent `extensions:` loadout) | Supported | Roadmap |
27
- | Plugins (agent `plugins:` loadout) | Supported | Roadmap |
28
- | Credentials and environment | Supported | Supported |
29
- | DeepWork job selection | Supported | Roadmap |
30
- | Hooks | Partial | Partial |
31
- | Tool availability (agent `tools:` loadout) | Supported | Supported |
32
- | Theme / UI presentation | Roadmap | Roadmap |
33
- | Working directory | Roadmap | Roadmap |
34
- | Pass-through arguments | Supported | Supported |
35
- | Bootstrap hook | Supported | Roadmap |
15
+ | What Outfitter projects | Pi | Claude Code | Codex CLI |
16
+ | ------------------------------------------------------------------------ | --------- | ----------- | --------- |
17
+ | Agent config directory | Supported | Supported | Roadmap |
18
+ | Session directory | Supported | Supported | Roadmap |
19
+ | Agent identity (`system-prompt.md`, `agents.md`, `agents/<id>/agent.md`) | Supported | Supported | Roadmap |
20
+ | Subagents (`agents/<id>` as harness delegates) | Supported | Supported | Roadmap |
21
+ | Skills (`skills/<id>`) | Supported | Partial | Roadmap |
22
+ | Commands (`commands/`) | Supported | Partial | Roadmap |
23
+ | Knowledge (`knowledge/`) | Supported | Partial | Roadmap |
24
+ | Model selection (`models.json`) | Supported | Partial | Partial |
25
+ | MCP servers (`mcp.json`) | Supported | Supported | Partial |
26
+ | Extensions (agent `extensions:` loadout) | Supported | Roadmap | Roadmap |
27
+ | Plugins (agent `plugins:` loadout) | Supported | Roadmap | Roadmap |
28
+ | Credentials and environment | Supported | Supported | Roadmap |
29
+ | DeepWork job selection | Supported | Roadmap | Roadmap |
30
+ | Hooks | Partial | Partial | Roadmap |
31
+ | Tool availability (agent `tools:` loadout) | Supported | Supported | Roadmap |
32
+ | Theme / UI presentation | Roadmap | Roadmap | Roadmap |
33
+ | Working directory | Roadmap | Roadmap | Roadmap |
34
+ | Pass-through arguments | Supported | Supported | Supported |
35
+ | Bootstrap hook | Supported | Roadmap | Roadmap |
36
+
37
+ ## Codex CLI notes
38
+
39
+ - **Launch mode** — Outfitter launches `codex` directly. Pass-through arguments choose the native mode: no subcommand keeps the interactive CLI shape, while `-- exec ...` selects non-interactive `codex exec`.
40
+ - **Agent identity and appended prompts** — Codex has no native identity projection yet: launches drop the composed identity/system prompt and any `--append-prompt` documents, supplied documents produce a separate warning, and `--strict` aborts before execution.
41
+ - **Model selection (Partial)** — an agent's model maps to `-m`. Provider maps have no projection element and produce no warning. Thinking, tools, skills, subagents, extensions, plugins, and prompt templates remain unsupported and warn when selected.
42
+ - **MCP servers (Partial)** — selected stdio fields (`command`, `args`, `env`, `cwd`) and streamable HTTP fields (`url`, `headers`) become repeated TOML-valued `-c mcp_servers.<id>.<key>=...` overrides. Server ids must contain only letters, digits, `_`, or `-`; other ids cannot be expressed by Codex `-c` key paths and are skipped with a warning. Legacy SSE and other HTTP transport types are also skipped with a warning. User and project `config.toml` servers remain active because Codex has no strict MCP isolation mode, so every launch warns that projection is additive, even when no servers are selected.
43
+ - **Stdio environment safety** — `${ENV_NAME}` becomes an `env_vars` reference only when the stdio `env` key is also `ENV_NAME`; a reference that would rename the variable is dropped with a warning. Literal values pass through `env` and are visible in process arguments.
44
+ - **HTTP header safety** — `${ENV_NAME}` becomes an `env_http_headers` reference, while `Authorization: Bearer ${ENV_NAME}` becomes `bearer_token_env_var`. Other header values pass through `http_headers` and are visible in process arguments. Outfitter warns for every literal stdio environment or HTTP header entry exposed in argv, so use environment references for secrets.
36
45
 
37
46
  ## Claude Code notes
38
47
 
39
- - **Config and session state** — Outfitter points `CLAUDE_CONFIG_DIR` at the baked composition, declares Claude state paths (`settings.json`, `agents/`, `skills/`, `commands/`, `plugins/`, `projects/`) for [state persistence](./state.md), and can [symlink a ported `~/.claude`](./porting-claude.md) so native use keeps working.
48
+ - **Config and session state** — Outfitter points `CLAUDE_CONFIG_DIR` at the baked composition. Before launch it copies only the current working directory's history from `~/.claude/projects/<project-slug>/` into the projection, so native `--continue` and `--resume` work without exposing other projects. After every successful or failed launch it atomically merges new or changed session files from every projected slug back into `~/.claude/projects/` with mode `0600`, never deleting durable history. Session bridge failures warn without masking the Claude exit. Outfitter also declares Claude state paths (`settings.json`, `agents/`, `skills/`, `commands/`, `plugins/`, `projects/`) for [state persistence](./state.md), and can [symlink a ported `~/.claude`](./porting-claude.md) so native use keeps working. MCP configuration from that port is no longer auto-discovered by Outfitter-launched Claude runs; those servers apply only when an agent selects them by slug. See the next bullet.
49
+ - **Credentials, onboarding, and workspace trust** — before launch, Outfitter copies `~/.claude/.credentials.json` to the temporary root as `.credentials.json` with mode `0600`. The projected `.claude.json` contains `oauthAccount` and `hasCompletedOnboarding` when those keys are present in durable `~/.claude.json`. It also contains `projects[<cwd>].hasTrustDialogAccepted: true` only when that exact accepted trust decision already exists there; other projects and unrelated machine state are not copied. After any successful or failed launch, a `.credentials.json` changed by the run is copied back wholesale and `oauthAccount` is atomically merged into durable `.claude.json` without replacing unrelated keys. If the durable credentials also changed after seeding, Outfitter preserves that concurrent refresh and warns instead of copying back. MCP OAuth tokens live under `mcpOAuth` in `.credentials.json`, keyed by `<serverName>|<hash>`, so authorizations acquired in an Outfitter-launched Claude session persist across runs. Other projected `.claude.json` state, including trust accepted during the session, is discarded; a workspace that has never been trusted by native Claude therefore prompts again on every run.
50
+ - **MCP servers** — every Claude launch passes the generated `mcp.json` through `--mcp-config` with `--strict-mcp-config`. MCP servers from user or project configuration, `.claude.json`, and plugins are therefore excluded; only servers selected by the composition are active.
40
51
  - **Subagents** — selected `agents/<id>` definitions are materialized into Claude's native agents directory.
41
52
  - **Skills (Partial)** — selected skills are materialized into the config directory's skills surface; remaining gaps are tracked per release. The bundled Outfitter skill ships through the plugin channel.
42
53
  - **Model selection (Partial)** — model maps to `--model` and thinking level to `--effort`; provider selection is not projected for Claude and warns if requested.
@@ -0,0 +1,143 @@
1
+ # Onboard an organization with an SDLC report
2
+
3
+ A runbook for the first engineer who brings agentic engineering to their
4
+ organization. The output is two artifacts: a baseline **SDLC report** that
5
+ says where the org sits on the [adoption ramp](../../philosophy.md), and the
6
+ org's **`.agents` repository** with that report as its first commit. The
7
+ report's gaps become the backlog; the repository becomes the place the org's
8
+ agent configuration lives from day one.
9
+
10
+ Who runs this: an engineer with read access to the org's repositories. No
11
+ org-wide rollout, approval, or infrastructure is required, and no agent
12
+ session either — the scan is one command. The whole runbook is one person,
13
+ one sitting, read-only until you create the repository.
14
+
15
+ ## 1. Prerequisites
16
+
17
+ - `npx` (node 20.19+) or Docker.
18
+ - An authenticated `gh` CLI with read access to the org.
19
+ - Optional but valuable: your existing local checkouts of org repositories.
20
+ The scanner reads them in addition to the forge API — local working trees
21
+ show practice the forge cannot see, such as `.agents/` trees in progress
22
+ and instruction files that were never committed.
23
+
24
+ ## 2. Run the assessment
25
+
26
+ [`@ai-outfitter/link`](https://github.com/ai-outfitter/link) audits the org
27
+ against the catalog's governance baseline. Write the report straight into
28
+ the dated directory it will be committed from:
29
+
30
+ ```sh
31
+ npx @ai-outfitter/link@1 report <org> \
32
+ --out ~/repos/<org>/.agents/reports/sdlc/$(date +%F)-initial
33
+ ```
34
+
35
+ Or with Docker, if you would rather not install anything:
36
+
37
+ ```sh
38
+ docker run --rm -e GH_TOKEN="$(gh auth token)" -v "$PWD:/work" \
39
+ ghcr.io/ai-outfitter/link:1 report <org>
40
+ ```
41
+
42
+ Add your local checkouts as sources to widen the evidence — a single repo, an
43
+ owner folder of clones, or a whole `~/repos/` root:
44
+
45
+ ```sh
46
+ npx @ai-outfitter/link@1 report <org> ~/repos/<org>
47
+ ```
48
+
49
+ The scan is read-only: it lists repositories, reads git trees, and reads
50
+ effective branch rules. It never clones and never writes to the forge. It
51
+ samples at most the 30 most recently pushed repositories, and takes seconds
52
+ rather than minutes.
53
+
54
+ Naming a target scopes the report to it, so the file you are about to commit
55
+ into `<org>`'s repository describes `<org>` and nothing else.
56
+
57
+ You get one file, `report.json`, plus a copy in
58
+ `$XDG_DATA_HOME/outfitter-link/`. It contains, for each repository, a
59
+ maturity-ramp placement (level 0–5), the tree-derived signals behind it
60
+ (instruction files, `.agents/` trees, agent workflows, deploy manifests), and
61
+ a per-rule audit against the governance baseline. At org level it carries the
62
+ milestones that gate each rung, the `gaps` blocking the next one, and
63
+ `evidence_limits` — what the scan could not see, which bounds every claim in
64
+ it.
65
+
66
+ Read the `gaps` before moving on. They name what blocks the next rung, not
67
+ the top of the ramp.
68
+
69
+ To see the report rendered, with the workflow definitions beside it, clone
70
+ the repository and run `link web`.
71
+
72
+ ### What the scan does not measure
73
+
74
+ The scanner decides everything from file trees and branch rules, so it is
75
+ fast, free, and reproducible — two runs of the same org agree apart from the
76
+ `generated_at` and `scanned_at` timestamps, which is what makes report diffs
77
+ a progress measure. The cost is that it reads no pull request history and
78
+ makes no judgments: no cycle time, no rework rate, no inventory of which
79
+ harnesses and model vendors are actually in use, and no duplication analysis
80
+ across teams.
81
+
82
+ When you want those, run the `sdlc-report` skill on a local coding harness as
83
+ a second, deeper pass. It answers the same question with an agent's judgment
84
+ instead of a checker's rules, and it emits recommendations. Start with
85
+ `link` — it is the cheap, repeatable baseline, and it is the one you will
86
+ re-run.
87
+
88
+ ## 3. Create the org `.agents` repository
89
+
90
+ Create `<org>/.agents` on your forge and commit the report as its first
91
+ content. Repository hygiene, learned the hard way:
92
+
93
+ - The repository MUST NOT be public — private or internal visibility only.
94
+ The report is an honest map of your org's gaps. (This organization
95
+ publishes its own report deliberately, as a worked example. That is a
96
+ choice about a reference; it is not the default.)
97
+ - If the repository already exists, commit only the report files. Leave any
98
+ uncommitted work in the checkout untouched, and if the default branch is
99
+ behind or checked out elsewhere, say so rather than silently moving it.
100
+
101
+ ```text
102
+ <org>/.agents/
103
+ README.md # what this repo is; link to the report
104
+ reports/
105
+ sdlc/
106
+ YYYY-MM-DD-initial/
107
+ report.json
108
+ ```
109
+
110
+ The initial report is the baseline: re-run the scan after each change (a
111
+ quarterly cadence works, or after each rung climb) into a new dated
112
+ directory, and the diff between reports is your progress measure — milestones
113
+ met and rung movements, not anecdotes.
114
+
115
+ This repository is also where the org's shared agent configuration grows: an
116
+ `agents.md` with shared operating rules, role agents, skills, and a pinned
117
+ `settings.yml`, following the [organization catalog](./organization-profile-catalog.md)
118
+ conventions. Starting it with the report means the catalog's first commit
119
+ explains _why_ the org is adopting agents and what it will measure — every
120
+ later addition traces back to a gap in the baseline.
121
+
122
+ ## 4. Act on the report
123
+
124
+ 1. Take the first entry in `gaps` and automate that one workflow end to end —
125
+ for example, feature idea → reviewed PR ([Actions](../actions.md),
126
+ [in-cluster](../in-cluster.md)).
127
+ 2. Add the shared resources the org lacks, so the next team composes instead
128
+ of rebuilding. A repo whose `signals.catalog` is false is a candidate.
129
+ 3. Wire session-log capture into the automated workflow before merge. The
130
+ `session-capture` milestone is unmet in almost every first report, and it
131
+ stays unmeasurable until workflows upload session artifacts behind a
132
+ required check. Owning that record is what makes the next report richer,
133
+ and it is the raw material for evals and improvement
134
+ ([philosophy](../../philosophy.md)).
135
+ 4. Schedule the re-run ([recurring runs](../recurring-runs.md)) and commit
136
+ each new report beside the baseline.
137
+
138
+ ## Boundaries
139
+
140
+ The scan is read-only; creating the `.agents` repository in step 3 is the
141
+ runbook's first write, done by you deliberately. The report contains repo
142
+ names, paths, and counts, never credentials or session content. Treat it as
143
+ internal: it is an honest map of your org's gaps.
@@ -91,7 +91,7 @@ outfitter run persona-reviewer --append-prompt docs/personas/platform-lead.md --
91
91
  > docs/persona-reviews/platform-lead-onboarding.md
92
92
  ```
93
93
 
94
- This is the portable interface: it works from the project containing the persona, does not assume a particular catalog checkout path, and does not assume a harness — `--append-prompt` projects the document through whichever flag pi or Claude Code actually reads. Repeat it to compose an identity from several documents; see [When one file is not enough](../personas.md#when-one-file-is-not-enough). One shared agent adopts the file as its identity for that session only and writes a first-person, sourced report — evidence cited to the exact page or UI moment, assumptions labeled. The reviewer inherits the caller's configured model; reviews benefit from a strong reasoning model.
94
+ This is the portable interface for pi and Claude Code: it works from the project containing the persona, does not assume a particular catalog checkout path, and projects `--append-prompt` through the native flag each harness reads. The Codex adapter has no native append flag and warns that the document is dropped. Repeat the option to compose an identity from several documents; see [When one file is not enough](../personas.md#when-one-file-is-not-enough). One shared agent adopts the file as its identity for that session only and writes a first-person, sourced report — evidence cited to the exact page or UI moment, assumptions labeled. The reviewer inherits the caller's configured model; reviews benefit from a strong reasoning model.
95
95
 
96
96
  ### Optional orchestration with the skill
97
97
 
@@ -2,6 +2,14 @@
2
2
 
3
3
  Make, share, and switch the agent profiles your coding agents use — manually or programmatically. A profile is just an agent and the loadout it composes; there is no separate profile format.
4
4
 
5
+ ## Trust through evidence
6
+
7
+ An agent is trusted the same way a new teammate is: small scopes, reviewed work, and a paper trail. Outfitter treats all three as configuration. A profile bounds what an agent can do per environment — the planning agent that has write tools at your desk has none in the cluster. Adversarial review is a workflow step rather than a virtue, and workflows are built so that every transition writes to the record.
8
+
9
+ ## Own your session data
10
+
11
+ The session record — what was asked, what the agent did, what it touched, what the review found — serves you as much as it serves the auditor. It answers "what happened and was it allowed" for an audit, and the same records are the inputs to evals, policy tuning, and eventually training. Organizations that let session data evaporate at the end of each run discard the asset that makes the whole system improvable. Store clean records first and decide on dashboards later — dashboards can always be built over clean records; records cannot be reconstructed from dashboards.
12
+
5
13
  ## Expeditious agents
6
14
 
7
15
  An agent is defined by its agency: its ability to make good decisions on the way to completing a task. Outfitter exists to make agents _expeditious_ — not just effective, but fast.
@@ -23,3 +31,15 @@ The same mechanism scales up a stair-step:
23
31
  - **Enterprises** publish and pin curated catalogs, keeping agent configuration reviewable, versioned, and consistent across the organization.
24
32
 
25
33
  At every level the goal is the same: the right profile, at the right moment, with nothing extra along for the ride.
34
+
35
+ ## The ramp to an autonomous lifecycle
36
+
37
+ Outfitter's destination is a fully autonomous software development lifecycle: humans define goals and acceptance gates, agents own the middle. Nobody jumps there in one step. Adoption is a ramp with five rungs, and each Outfitter component targets a rung, so a user or an organization climbs without discarding the previous rung. This section is the canonical definition; the org README and the `sdlc-report` assessment skill compress or extend it.
38
+
39
+ 1. **Assisted** — autocomplete and chat; a human's hands stay on the keyboard.
40
+ 2. **Delegated** — a local agent does the task; the human defines the idea and reviews the PR.
41
+ 3. **Automated** — a workflow runs without a laptop: an issue, a message, or a schedule triggers agents in CI or a cluster, and adversarial review is part of the pipeline.
42
+ 4. **Governed** — the organization shares one pinned catalog of agents, skills, and policy; every agent action lands in an auditable record; resident agents work as onboarded teammates.
43
+ 5. **Self-improving** — the audit record feeds evals and model improvement; humans set goals and acceptance gates, agents own the middle.
44
+
45
+ Two rules keep the climb honest. Never automate a workflow you have not first done manually — run it as an agent-assisted skill until you understand it, then promote it. And expand scope by moving the human locus of control outward one layer at a time: first the implementation, then the review, then the idea, until what remains human is the goal and the gate.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-outfitter/outfitter",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Profile-oriented wrapper for launching pi, Claude Code, and future agent CLIs with reproducible configuration.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -5,7 +5,7 @@
5
5
  "type": "object",
6
6
  "properties": {
7
7
  "default_agent": { "type": "string", "minLength": 1 },
8
- "default_harness": { "enum": ["pi", "claude"] },
8
+ "default_harness": { "enum": ["pi", "claude", "codex"] },
9
9
  "cache_directory": { "type": "string", "minLength": 1 },
10
10
  "state_persistence": {
11
11
  "type": "object",
@@ -0,0 +1,38 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://outfitter.dev/schemas/system-extension-hook.schema.json",
4
+ "title": "Outfitter system extension hook",
5
+ "type": "object",
6
+ "required": ["name", "harnesses"],
7
+ "properties": {
8
+ "name": { "type": "string", "minLength": 1 },
9
+ "harnesses": {
10
+ "type": "object",
11
+ "minProperties": 1,
12
+ "properties": {
13
+ "pi": { "$ref": "#/$defs/harnessHook" },
14
+ "claude": { "$ref": "#/$defs/harnessHook" },
15
+ "codex": { "$ref": "#/$defs/harnessHook" }
16
+ },
17
+ "additionalProperties": false
18
+ }
19
+ },
20
+ "additionalProperties": false,
21
+ "$defs": {
22
+ "harnessHook": {
23
+ "type": "object",
24
+ "properties": {
25
+ "extensions": {
26
+ "type": "array",
27
+ "items": { "type": "string", "pattern": "^/" }
28
+ },
29
+ "env": {
30
+ "type": "object",
31
+ "propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
32
+ "additionalProperties": { "type": "string" }
33
+ }
34
+ },
35
+ "additionalProperties": false
36
+ }
37
+ }
38
+ }