@ai-outfitter/outfitter 1.14.0 → 1.16.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.
- package/README.md +8 -4
- package/code/pi-extension/src/outfitter-extension.js +155 -65
- package/code/pi-extension/src/outfitter-runtime-extension.js +41 -25
- package/dist/cli/OutfitterCli.js +2 -0
- package/dist/cli/OutfitterCli.js.map +1 -1
- package/dist/cli/commands/DumpCommand.js +2 -2
- package/dist/cli/commands/DumpCommand.js.map +1 -1
- package/dist/cli/commands/LinkCommand.d.ts +27 -0
- package/dist/cli/commands/LinkCommand.js +137 -0
- package/dist/cli/commands/LinkCommand.js.map +1 -0
- package/dist/cli/commands/ListCommand.d.ts +2 -0
- package/dist/cli/commands/ListCommand.js +32 -8
- package/dist/cli/commands/ListCommand.js.map +1 -1
- package/dist/cli/commands/PiRuntimeLaunch.d.ts +6 -0
- package/dist/cli/commands/PiRuntimeLaunch.js +11 -1
- package/dist/cli/commands/PiRuntimeLaunch.js.map +1 -1
- package/dist/cli/commands/RunAgentCommand.d.ts +2 -0
- package/dist/cli/commands/RunAgentCommand.js +36 -14
- package/dist/cli/commands/RunAgentCommand.js.map +1 -1
- package/dist/cli/commands/SetupCommand.d.ts +22 -1
- package/dist/cli/commands/SetupCommand.js +97 -8
- package/dist/cli/commands/SetupCommand.js.map +1 -1
- package/dist/cli/commands/ValidateCommand.js +4 -1
- package/dist/cli/commands/ValidateCommand.js.map +1 -1
- package/dist/cli.js +14 -6
- package/dist/cli.js.map +1 -1
- package/dist/composer/Chain.d.ts +10 -0
- package/dist/composer/Chain.js +47 -0
- package/dist/composer/Chain.js.map +1 -0
- package/dist/composer/Composer.d.ts +6 -0
- package/dist/composer/Composer.js +42 -141
- package/dist/composer/Composer.js.map +1 -1
- package/dist/composer/Composition.d.ts +16 -0
- package/dist/composer/Defaults.d.ts +33 -0
- package/dist/composer/Defaults.js +103 -0
- package/dist/composer/Defaults.js.map +1 -0
- package/dist/composer/Mcp.d.ts +3 -0
- package/dist/composer/Mcp.js +68 -0
- package/dist/composer/Mcp.js.map +1 -0
- package/dist/dump/Dump.d.ts +2 -1
- package/dist/dump/Dump.js +67 -4
- package/dist/dump/Dump.js.map +1 -1
- package/dist/dump/WorkflowDump.d.ts +9 -1
- package/dist/dump/WorkflowDump.js +7 -3
- package/dist/dump/WorkflowDump.js.map +1 -1
- package/dist/links/HarnessHome.d.ts +15 -0
- package/dist/links/HarnessHome.js +23 -0
- package/dist/links/HarnessHome.js.map +1 -0
- package/dist/links/HarnessLinkApply.d.ts +26 -0
- package/dist/links/HarnessLinkApply.js +356 -0
- package/dist/links/HarnessLinkApply.js.map +1 -0
- package/dist/links/HarnessLinkPlan.d.ts +70 -0
- package/dist/links/HarnessLinkPlan.js +224 -0
- package/dist/links/HarnessLinkPlan.js.map +1 -0
- package/dist/links/HarnessMcp.d.ts +9 -0
- package/dist/links/HarnessMcp.js +67 -0
- package/dist/links/HarnessMcp.js.map +1 -0
- package/dist/projection/CodexSettings.d.ts +3 -0
- package/dist/projection/CodexSettings.js +17 -0
- package/dist/projection/CodexSettings.js.map +1 -0
- package/dist/projection/Materialize.d.ts +20 -2
- package/dist/projection/Materialize.js +66 -33
- package/dist/projection/Materialize.js.map +1 -1
- package/dist/projection/ProjectHarness.js +58 -14
- package/dist/projection/ProjectHarness.js.map +1 -1
- package/dist/projection/Projection.d.ts +3 -1
- package/dist/resolver/ResolverValidation.d.ts +5 -0
- package/dist/resolver/ResolverValidation.js +45 -10
- package/dist/resolver/ResolverValidation.js.map +1 -1
- package/dist/resolver/WorkflowDefinition.d.ts +10 -0
- package/dist/resolver/WorkflowDefinition.js.map +1 -1
- package/dist/resolver/WorkflowOutput.d.ts +9 -0
- package/dist/resolver/WorkflowOutput.js +40 -0
- package/dist/resolver/WorkflowOutput.js.map +1 -0
- package/dist/schemas/settings.schema.json +48 -1
- package/dist/schemas/workflow.schema.json +26 -0
- package/dist/settings/Settings.d.ts +22 -0
- package/dist/settings/Settings.js +14 -0
- package/dist/settings/Settings.js.map +1 -1
- package/dist/settings/SettingsLoader.js +17 -0
- package/dist/settings/SettingsLoader.js.map +1 -1
- package/dist/settings/SettingsMerger.js +37 -0
- package/dist/settings/SettingsMerger.js.map +1 -1
- package/dist/setup/DefaultCatalog.d.ts +4 -2
- package/dist/setup/DefaultCatalog.js +5 -3
- package/dist/setup/DefaultCatalog.js.map +1 -1
- package/dist/setup/Setup.d.ts +11 -2
- package/dist/setup/Setup.js +51 -39
- package/dist/setup/Setup.js.map +1 -1
- package/dist/version/NodeVersionGuard.d.ts +13 -0
- package/dist/version/NodeVersionGuard.js +50 -0
- package/dist/version/NodeVersionGuard.js.map +1 -0
- package/docs/architecture/state_writeback_strategy.md +1 -1
- package/docs/documentation/README.md +1 -1
- package/docs/documentation/catalogs.md +36 -1
- package/docs/documentation/cli.md +42 -7
- package/docs/documentation/conventions.md +1 -1
- package/docs/documentation/dump-and-bake.md +1 -1
- package/docs/documentation/getting-started.md +4 -2
- package/docs/documentation/linking-harnesses.md +88 -0
- package/docs/documentation/local-development.md +5 -7
- package/docs/documentation/migration.md +1 -1
- package/docs/documentation/settings.md +68 -3
- package/docs/documentation/state.md +1 -1
- package/docs/documentation/support-matrix.md +3 -1
- package/docs/documentation/switching-to-outfitter.md +1 -1
- package/docs/documentation/usecases/shared-conventions.md +1 -1
- package/package.json +2 -1
- package/src/schemas/settings.schema.json +48 -1
- package/src/schemas/workflow.schema.json +26 -0
- package/docs/documentation/porting-claude.md +0 -58
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Refuses to run on a Node release below the published `engines.node` floor.
|
|
2
|
+
//
|
|
3
|
+
// npm treats an engines mismatch as a warning, so `npm install -g` and `npx` succeed on
|
|
4
|
+
// old Node. Outfitter's own commands then work, and the process only crashes deep inside
|
|
5
|
+
// the bundled pi once `run` or `setup` spawns it (issue #368). This guard turns that stack
|
|
6
|
+
// trace into a one-line message before any command, including the telemetry notice, runs.
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
// Always yields three parts so comparisons never index past the end.
|
|
9
|
+
const parseVersion = (version) => {
|
|
10
|
+
const [major = 0, minor = 0, patch = 0] = version
|
|
11
|
+
.replace(/^v/u, '')
|
|
12
|
+
.split('.')
|
|
13
|
+
.map((part) => Number.parseInt(part, 10) || 0);
|
|
14
|
+
return [major, minor, patch];
|
|
15
|
+
};
|
|
16
|
+
// `engines.node` is required by OFTR-001.1 to be an unbounded `>=x.y.z` range, so only
|
|
17
|
+
// that shape is supported. Anything else is treated as satisfied rather than blocking.
|
|
18
|
+
const parseMinimum = (range) => {
|
|
19
|
+
const match = /^>=\s*v?(\d+\.\d+\.\d+)$/u.exec(range.trim());
|
|
20
|
+
return match === null ? undefined : parseVersion(match[1]);
|
|
21
|
+
};
|
|
22
|
+
export const readRequiredNodeRange = () => {
|
|
23
|
+
const packageJsonPath = new URL('../../package.json', import.meta.url);
|
|
24
|
+
const manifest = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
25
|
+
return manifest.engines.node;
|
|
26
|
+
};
|
|
27
|
+
export const checkNodeVersion = (current, required) => {
|
|
28
|
+
const minimum = parseMinimum(required);
|
|
29
|
+
if (minimum === undefined)
|
|
30
|
+
return { required, current, satisfied: true };
|
|
31
|
+
const actual = parseVersion(current);
|
|
32
|
+
const first = minimum.findIndex((part, index) => part !== actual[index]);
|
|
33
|
+
const satisfied = first === -1 || actual[first] > minimum[first];
|
|
34
|
+
return { required, current, satisfied };
|
|
35
|
+
};
|
|
36
|
+
export const formatNodeVersionError = (check) => [
|
|
37
|
+
`Outfitter requires Node ${check.required} but found v${check.current}.`,
|
|
38
|
+
'Upgrade Node (for example with `nvm install --lts`) and run the command again.',
|
|
39
|
+
].join('\n');
|
|
40
|
+
/**
|
|
41
|
+
* Prints the upgrade message and returns false when the running Node is below the
|
|
42
|
+
* published floor. Callers must not import or spawn pi before this returns true.
|
|
43
|
+
*/
|
|
44
|
+
export const enforceNodeVersion = (current = process.versions.node, required = readRequiredNodeRange(), writeError = console.error) => {
|
|
45
|
+
const check = checkNodeVersion(current, required);
|
|
46
|
+
if (!check.satisfied)
|
|
47
|
+
writeError(formatNodeVersionError(check));
|
|
48
|
+
return check.satisfied;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=NodeVersionGuard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeVersionGuard.js","sourceRoot":"","sources":["../../src/version/NodeVersionGuard.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,wFAAwF;AACxF,yFAAyF;AACzF,2FAA2F;AAC3F,0FAA0F;AAC1F,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQvC,qEAAqE;AACrE,MAAM,YAAY,GAAG,CAAC,OAAe,EAAqC,EAAE;IAC1E,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,OAAO;SAC9C,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAC/B,CAAC,CAAC;AAEF,uFAAuF;AACvF,uFAAuF;AACvF,MAAM,YAAY,GAAG,CAAC,KAAa,EAAiD,EAAE;IACpF,MAAM,KAAK,GAAG,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7D,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAW,EAAE;IAChD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAEhE,CAAC;IACF,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAE,QAAgB,EAAoB,EAAE;IACtF,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAEzE,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACzE,MAAM,SAAS,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACjE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAC1C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAuB,EAAU,EAAE,CACxE;IACE,2BAA2B,KAAK,CAAC,QAAQ,eAAe,KAAK,CAAC,OAAO,GAAG;IACxE,gFAAgF;CACjF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEf;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,UAAkB,OAAO,CAAC,QAAQ,CAAC,IAAI,EACvC,WAAmB,qBAAqB,EAAE,EAC1C,aAAwC,OAAO,CAAC,KAAK,EAC5C,EAAE;IACX,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,UAAU,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC;IAChE,OAAO,KAAK,CAAC,SAAS,CAAC;AACzB,CAAC,CAAC"}
|
|
@@ -68,7 +68,7 @@ Outfitter defaults
|
|
|
68
68
|
|
|
69
69
|
Native CLI state is not a configuration layer. For `symlink` paths, the selected adapter resolves a native fallback location directly, such as `~/.pi/agent/...` for most Pi state paths, `~/.claude/...` for most Claude Code state paths, or `<cache_directory>/utilities` for Pi `utilities/` and `bin/`. The native fallback does not participate in resource resolution or merge precedence and cannot contribute resources. Claude Code `projects/` is additionally controlled by the session-directory setting when set.
|
|
70
70
|
|
|
71
|
-
For a [
|
|
71
|
+
For a [linked Claude Code home](../documentation/linking-harnesses.md), managed configuration entries under `~/.claude` are symlinks into `~/.agents/`, so a durable write through the projection's `skills/` link lands in the protocol tree. The links are created by `outfitter link`, not setup; the state machinery just follows them.
|
|
72
72
|
|
|
73
73
|
## Path-keyed adapter declarations
|
|
74
74
|
|
|
@@ -23,7 +23,7 @@ One runbook per rung of [the adoption ramp](../philosophy.md#the-ramp-to-an-auto
|
|
|
23
23
|
- [Getting started](./getting-started.md) — install, first run, default agent.
|
|
24
24
|
- [First-time CLI agent users](./first-time-cli-agent-users.md) — new to agent CLIs entirely.
|
|
25
25
|
- [Switching to Outfitter](./switching-to-outfitter.md) — adopt from an existing agent-CLI setup.
|
|
26
|
-
- [
|
|
26
|
+
- [Linking into Claude Code and Codex](./linking-harnesses.md) — `outfitter link` places managed links to the tree in `~/.claude` and `~/.codex`.
|
|
27
27
|
|
|
28
28
|
## Understand (the model)
|
|
29
29
|
|
|
@@ -89,7 +89,42 @@ Resources from all sources resolve by slug behind local layers, following [layer
|
|
|
89
89
|
|
|
90
90
|
Each `workflows/<slug>/workflow.yaml` is a typed graph that names its human, agent, tool, and system actors. Agent actors reference ordinary catalog profiles. Node-level skill, prompt, and MCP assertions must already belong to the selected agent's composed closure. Nested workflow references resolve by slug and may not form cycles.
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
A workflow can publish output declarations. Output names must start with a lowercase letter and
|
|
93
|
+
otherwise follow the workflow node ID pattern. An output's `type` is an optional plain label: an
|
|
94
|
+
action node declares it directly, while a nested-workflow node maps one of the nested workflow's
|
|
95
|
+
declared outputs and inherits its label:
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
outputs:
|
|
99
|
+
pull-request:
|
|
100
|
+
from: draft # an action node in this workflow
|
|
101
|
+
type: pull-request
|
|
102
|
+
review-verdict:
|
|
103
|
+
from: review # a nested-workflow node in this workflow
|
|
104
|
+
output: verdict # declared by the nested workflow
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The conventional labels for forge objects are `pull-request`, `git-commit`, `git-branch`, and
|
|
108
|
+
`issue`, so workflows use consistent names for the same kinds of objects. An organization may use
|
|
109
|
+
any slug as an output type label. A mapped output inherits the nested output's resolved label,
|
|
110
|
+
including through multiple nesting levels.
|
|
111
|
+
|
|
112
|
+
A node's `needs` list expresses ordering only among nodes in the same workflow. Cross-task
|
|
113
|
+
prerequisites are an execution engine's responsibility: the engine evaluates them against declared
|
|
114
|
+
outputs rather than treating a workflow node edge as a task dependency.
|
|
115
|
+
|
|
116
|
+
`outfitter validate --strict` validates the graph, output mappings, and complete composed dependency
|
|
117
|
+
closure. `outfitter dump --workflow <slug>` produces a reviewable `.agents` bundle for distribution.
|
|
118
|
+
Outfitter never schedules or executes the graph. See
|
|
119
|
+
[OFTR-013: Workflow Contract](../requirements/OFTR-013-workflow-contract.md) for the normative
|
|
120
|
+
contract.
|
|
121
|
+
|
|
122
|
+
#### Recording values
|
|
123
|
+
|
|
124
|
+
Outfitter declares outputs but does not record or validate their concrete values; that belongs to the
|
|
125
|
+
execution engine. A runtime carrying a value over A2A should use `outfitter-task/v1` artifact metadata
|
|
126
|
+
with `output` set to the declared name, `type` set to its resolved label, and `value` set to the
|
|
127
|
+
concrete value.
|
|
93
128
|
|
|
94
129
|
### Catalog dependencies (transitive sources)
|
|
95
130
|
|
|
@@ -42,13 +42,14 @@ outfitter --resume # equivalent to: outfitter run -- --resume
|
|
|
42
42
|
|
|
43
43
|
## `outfitter setup [source]`
|
|
44
44
|
|
|
45
|
-
Open the bundled Pi walkthrough
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
CLI agent. Pi/Outfitter is preselected.
|
|
45
|
+
Open the bundled Pi walkthrough. Choose a featured profile from the default catalog (Engineer,
|
|
46
|
+
Founder, or Software Factory; Engineer is preselected), open **More profiles** for the rest, or
|
|
47
|
+
**Import a different .agents catalog**; complete that branch; choose a home/project
|
|
48
|
+
settings target; then choose the default CLI agent. Pi/Outfitter is preselected. To use a custom
|
|
49
|
+
profile instead, write `.agents/agents/<id>/agent.md` and set `default_agent`. Passing `[source]` retains the original direct-source path
|
|
49
50
|
and starts at target selection. Pi hosts the deterministic setup UI without a model provider and
|
|
50
51
|
does not port or symlink harness configuration. The default picker always comes from
|
|
51
|
-
`ai-outfitter/
|
|
52
|
+
`ai-outfitter/community-profiles` at the immutable Release Please version tag pinned by the installed
|
|
52
53
|
Outfitter version; setup fetches or reuses that release through the normal source cache and writes
|
|
53
54
|
the same GitHub/ref pair to settings. It never reads a sibling checkout or a packaged catalog
|
|
54
55
|
fallback.
|
|
@@ -75,7 +76,7 @@ List resolvable resources across all layers, with the winning source for each sl
|
|
|
75
76
|
| -------- | -------------------------------------------------------------------------- |
|
|
76
77
|
| `[kind]` | Optional filter: `agents`, `skills`, `knowledge`, `commands`, `workflows`. |
|
|
77
78
|
|
|
78
|
-
`--json` emits an object containing `ok`, `resources`, and `diagnostics`; diagnostics remain available when strict mode fails.
|
|
79
|
+
`--json` emits an object containing `ok`, `resources`, and `diagnostics`; diagnostics remain available when strict mode fails. Each workflow resource entry also contains a name-sorted `outputs` object with resolved output labels, or `{}` when the workflow declares none. Non-JSON output is unchanged. See [OFTR-013: Workflow Contract](../requirements/OFTR-013-workflow-contract.md).
|
|
79
80
|
|
|
80
81
|
## `outfitter validate`
|
|
81
82
|
|
|
@@ -96,13 +97,47 @@ Write the composed resource tree as a self-contained `.agents/` directory for re
|
|
|
96
97
|
| `--workflow <id>` | Export one workflow, its nested workflows, and every referenced agent closure. |
|
|
97
98
|
| `--out <dir>` | Destination directory (default `./.agents`). |
|
|
98
99
|
|
|
99
|
-
Workflow dumps are non-executable configuration bundles. They contain the canonical workflow YAML, composed agent resources, and a hash/provenance manifest
|
|
100
|
+
Workflow dumps are non-executable configuration bundles. They contain the canonical workflow YAML, composed agent resources, and a hash/provenance manifest whose `workflows[]` entries record resolved `outputs`. A workflow dump refuses an existing destination instead of replacing user files.
|
|
100
101
|
|
|
101
102
|
> **Tasks and `outfitter task bake`** — baking a task and its inputs into an immutable execution artifact — are the subject of a separate upcoming RFC and are not part of this command surface yet. See [Tasks](./tasks.md).
|
|
102
103
|
|
|
103
104
|
`outfitter run` verifies these caches before composition. Use
|
|
104
105
|
`--source-cache-policy <repair|locked|offline>` to override the configured startup policy.
|
|
105
106
|
|
|
107
|
+
## `outfitter link`
|
|
108
|
+
|
|
109
|
+
Project composed resources and native defaults into Pi, Claude Code, and Codex homes, so plain
|
|
110
|
+
`pi`, `claude`, and `codex` sessions carry shared configuration
|
|
111
|
+
without going through `outfitter run`. `run` still uses a temporary projection; `link` is the
|
|
112
|
+
opt-in persistent one. See [Linking into Claude Code and Codex](./linking-harnesses.md).
|
|
113
|
+
|
|
114
|
+
| Option | Description |
|
|
115
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
116
|
+
| `--harness <name>` | Harness home to link into: `pi`, `claude`, or `codex` (repeatable). Defaults to every harness on `PATH` or with an existing home. |
|
|
117
|
+
| `--agent <id>` | Agent whose composed closure to link (repeatable). |
|
|
118
|
+
| `--workflow <id>` | Enabled workflow whose agent closures to link (repeatable). |
|
|
119
|
+
| `--all` | Link every resolvable agent, with its skills and commands. |
|
|
120
|
+
| `--dry-run` | Report what would change (`would create`, `would update`, `would prune`) without touching the home. |
|
|
121
|
+
| `--remove` | Remove every entry this command created and forget it. |
|
|
122
|
+
| `--strict` | Exit non-zero on warnings, conflicts, or skipped entries. |
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
outfitter link # enabled workflows + default_agent, every installed harness
|
|
126
|
+
outfitter link --workflow engineer --harness claude
|
|
127
|
+
outfitter link --all --dry-run
|
|
128
|
+
outfitter link --remove
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
With no selection the scope is every enabled workflow root (`workflows:` in settings) plus
|
|
132
|
+
`default_agent`. Each scoped agent is composed the same way `run` and `dump` compose it, and its
|
|
133
|
+
subagents join the closure. The harness home is `$CLAUDE_CONFIG_DIR` (default `~/.claude`) or
|
|
134
|
+
`$CODEX_HOME` (default `~/.codex`); an explicit `--harness` creates the home if it is missing.
|
|
135
|
+
|
|
136
|
+
Ownership is recorded in `<harness home>/.outfitter/links.json`. `link` never overwrites, adopts, or
|
|
137
|
+
deletes anything it did not create: an unmanaged file, directory, or symlink in the way is reported
|
|
138
|
+
as a `conflict` and left alone. Re-running is idempotent (`unchanged`), a managed link whose target
|
|
139
|
+
vanished is `pruned`, and MCP servers already registered in the harness are left as they are.
|
|
140
|
+
|
|
106
141
|
## `outfitter sources`
|
|
107
142
|
|
|
108
143
|
Report local and remote source precedence, requested and resolved revisions, origins, and cache
|
|
@@ -15,7 +15,7 @@ Each layer inherits the one above it. ID-addressed resources — agents, skills,
|
|
|
15
15
|
| Layer | Location | Holds |
|
|
16
16
|
| ---------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
17
17
|
| Community | e.g. `ai-outfitter/community-profiles` | Reviewed building blocks — skills and reference agents anyone can mix and match. |
|
|
18
|
-
| Curated defaults | e.g. `ai-outfitter/
|
|
18
|
+
| Curated defaults | e.g. `ai-outfitter/community-profiles` | The pinned, curated assembly — starter agents users adopt as-is. |
|
|
19
19
|
| Organization | `owner/.outfitter` [control repo](./usecases/organization-profile-catalog.md) | Bespoke org agents, shared `agents.md`, org-specific skills (brand voice, RBAC, endpoints). |
|
|
20
20
|
| User / project | `~/.agents`, `<repo>/.agents` | Personal and repo overrides, loadout-added references, same-ID resource overrides. |
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ Use dumps to:
|
|
|
25
25
|
- **Safe** — a dump may include reviewable source provenance, but never credentials, auth state, sessions, transcripts, caches, backups, mutable harness state, or symlinks escaping the tree.
|
|
26
26
|
- **Protocol-shaped** — the output is a valid `.agents` payload usable by any protocol consumer, not just Outfitter. Any Outfitter-specific provenance metadata is namespaced, JSON-based, and removable without losing the underlying resources.
|
|
27
27
|
- **Harness-discoverable** — selected agent-local skills are flattened into top-level `skills/<id>/` in the closure output, with their packaged references, scripts, and assets intact.
|
|
28
|
-
- **Workflow-auditable** — workflow exports preserve canonical YAML and record every nested workflow, agent composition, file hash, and winning source in `.outfitter/workflow-composition.json`.
|
|
28
|
+
- **Workflow-auditable** — workflow exports preserve canonical YAML and record every nested workflow, its resolved `outputs`, agent composition, file hash, and winning source in `.outfitter/workflow-composition.json`. Each `workflows[]` entry has an `outputs` object (empty when none are declared). The manifest records only outputs that resolve, and `outfitter validate --strict` is the gate that reports declarations which do not. See [OFTR-013: Workflow Contract](../requirements/OFTR-013-workflow-contract.md).
|
|
29
29
|
|
|
30
30
|
## Bake
|
|
31
31
|
|
|
@@ -20,11 +20,13 @@ outfitter run # launch with your defaults
|
|
|
20
20
|
|
|
21
21
|
Set `default_agent` in `.agents/settings.yml` to one of your [agent](./agents.md) slugs; that agent's own loadout selects the skills, subagents, model, and so on it runs with. You're done.
|
|
22
22
|
|
|
23
|
-
If your configuration lives in `~/.claude` instead,
|
|
23
|
+
If your configuration lives in `~/.claude` instead, move it into `~/.agents/` and run `outfitter link` so Claude Code reads the tree natively — see [Linking into Claude Code and Codex](./linking-harnesses.md).
|
|
24
24
|
|
|
25
25
|
## First-time setup
|
|
26
26
|
|
|
27
|
-
Bootstrap from the Outfitter
|
|
27
|
+
Bootstrap from the Outfitter
|
|
28
|
+
[default catalog](https://github.com/ai-outfitter/community-profiles), then launch
|
|
29
|
+
the default agent:
|
|
28
30
|
|
|
29
31
|
```bash
|
|
30
32
|
outfitter setup
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Linking into Pi, Claude Code, and Codex
|
|
2
|
+
|
|
3
|
+
`outfitter run` projects a composition into a temporary directory for one launch. `outfitter link` is the persistent form: it places managed resources and native defaults inside a harness's own home, so plain `pi`, `claude`, and `codex` sessions can use the shared configuration with no Outfitter in the loop.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
outfitter link # every enabled workflow + default_agent, into every installed harness
|
|
7
|
+
outfitter link --workflow engineer --harness claude # one workflow's agent closures, Claude only
|
|
8
|
+
outfitter link --all --dry-run # preview linking every resolvable agent
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The `.agents` tree stays the source of truth. Links point into it; nothing is copied and diverged.
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
With no selection, `link` takes what settings already opted into: every enabled workflow root (`workflows:` in `settings.yml`) plus `default_agent`. `--workflow` must name an enabled workflow; `--agent` names any resolvable agent; `--all` widens the scope to every resolvable agent. Each scoped agent is composed by the same composer `run` and `dump` use, and every subagent it delegates to joins the closure — with its own skills, commands, and MCP servers.
|
|
16
|
+
|
|
17
|
+
The harness home is `$PI_CODING_AGENT_DIR` (default `~/.pi/agent`) for Pi, `$CLAUDE_CONFIG_DIR` (default `~/.claude`) for Claude Code, and `$CODEX_HOME` (default `~/.codex`) for Codex. Without `--harness`, every harness whose executable is on `PATH` or whose home exists is linked; an explicit `--harness` creates the home if it is missing.
|
|
18
|
+
|
|
19
|
+
## What lands where
|
|
20
|
+
|
|
21
|
+
| Tree resource | Pi home | Claude Code home | Codex home | How |
|
|
22
|
+
| ------------------------------------------- | --------------- | -------------------------------------- | ------------------- | ----------------- |
|
|
23
|
+
| Winning tree-root `agents.md` | — (warning) | `CLAUDE.md` | `AGENTS.md` | symlink |
|
|
24
|
+
| Skill `skills/<slug>/` | `skills/<slug>` | `skills/<slug>` | `skills/<slug>` | symlink |
|
|
25
|
+
| Composed agent (scoped agent and delegates) | — (warning) | `agents/<slug>.md` | — (warning) | generated |
|
|
26
|
+
| Command (catalog and agent-local) | — (warning) | `commands/<slug>.md` | `prompts/<slug>.md` | symlink |
|
|
27
|
+
| Selected MCP server | — (warning) | `claude mcp add-json ... --scope user` | `codex mcp add ...` | registered |
|
|
28
|
+
| `harness_defaults` leaf | `settings.json` | `settings.json` | `config.toml` | managed per value |
|
|
29
|
+
|
|
30
|
+
- **Symlinked** entries resolve to the winning file or directory in the tree, so editing the tree edits what the harness reads.
|
|
31
|
+
- **Generated** entries are written, not linked. A Claude agent definition is a single Markdown file, but an Outfitter agent's identity is composed — system prompt, shared context, appended prompt fragments, inherited bodies — with frontmatter carrying `name`, `description`, `model`, `tools`, and `skills`. `link` writes that composition into `agents/<slug>.md` under a `Generated by outfitter link` marker. Edit the tree and relink; edits to the generated file are replaced. Codex has no native agent definitions, so agent identities are reported as a warning and not linked there.
|
|
32
|
+
- **Registered** entries go through the harness's own CLI so the harness owns the record. Claude takes the protocol definition verbatim at user scope. Codex takes what `codex mcp add` can express: stdio `command`, `args`, and literal `--env` values; HTTP `--url`, with `Authorization: Bearer ${VAR}` mapped to `--bearer-token-env-var`. Other headers and `${VAR}` env references are warned about and dropped rather than mis-registered.
|
|
33
|
+
|
|
34
|
+
Prompt fragments are not linked as standalone files: they are composed into each generated agent document. The always-on shared context is the `agents.md` link.
|
|
35
|
+
|
|
36
|
+
## What stays native
|
|
37
|
+
|
|
38
|
+
Runtime and account state is not configuration and is not touched:
|
|
39
|
+
|
|
40
|
+
- auth and account state
|
|
41
|
+
- sessions and project history (`projects/`)
|
|
42
|
+
- native settings not declared under `harness_defaults`; permissions, model, and hooks remain harness-native unless explicitly shared there
|
|
43
|
+
- plugins, caches, debug output
|
|
44
|
+
|
|
45
|
+
This is the same boundary [state persistence](./state.md) enforces at run time: configuration lives in the tree, mutable state lives with the harness.
|
|
46
|
+
|
|
47
|
+
## Ownership and conflicts
|
|
48
|
+
|
|
49
|
+
`link` records what it created in `<harness home>/.outfitter/links.json` and only ever acts on entries in that manifest. It never overwrites, adopts, or deletes anything it did not create. Each entry reports one status:
|
|
50
|
+
|
|
51
|
+
| Status | Meaning |
|
|
52
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `created` | Written or registered for the first time. |
|
|
54
|
+
| `updated` | A manifest entry whose target or content changed; only owned entries are ever updated. |
|
|
55
|
+
| `unchanged` | Already correct. MCP servers already present in the harness are `unchanged` and never modified. |
|
|
56
|
+
| `conflict` | An unmanaged file, directory, or symlink is in the way — including an unmanaged symlinked parent such as a whole `skills -> ...` directory link. The message says what to unlink. |
|
|
57
|
+
| `skipped` | The harness CLI is not on `PATH`, or its `mcp add` failed. |
|
|
58
|
+
| `pruned` | A managed symlink whose target vanished was removed and forgotten. |
|
|
59
|
+
|
|
60
|
+
Conflicts are reported, not resolved: move or unlink the unmanaged entry yourself, then relink. `--strict` exits 1 on any warning, conflict, or skipped entry, which is the form to use in scripts.
|
|
61
|
+
|
|
62
|
+
## Relinking
|
|
63
|
+
|
|
64
|
+
`link` is idempotent. After editing the tree, adding a skill to an agent, or bumping a source, run it again: unchanged entries stay put, changed generated files are rewritten, new closure members are created, and links whose targets disappeared are pruned. A second run with nothing changed reports everything `unchanged`.
|
|
65
|
+
|
|
66
|
+
`--dry-run` prints `would create`, `would update`, and `would prune` without touching the home; the only harness commands it runs are read-only (`mcp get`).
|
|
67
|
+
|
|
68
|
+
## Removing
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
outfitter link --remove
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Removes exactly the manifest entries — symlinks, generated agent files, individual native setting values, and MCP servers it added (through `mcp remove`) — and forgets them. Managed containers (`skills/`, `agents/`, `commands/`, `prompts/`) are removed when they are left empty. Anything unmanaged beside them is untouched.
|
|
75
|
+
|
|
76
|
+
## Migrating an existing `~/.claude`
|
|
77
|
+
|
|
78
|
+
`outfitter setup` does not port `~/.claude`. To bring an existing Claude Code setup under the tree: move your `skills/`, `agents/`, and `commands/` into `~/.agents/` (or a git repo linked there — see [Local development](./local-development.md)), reshaping agents as `agents/<id>/agent.md` and `CLAUDE.md` as `agents.md`; remove any whole-directory symlinks such as `~/.claude/skills -> ...`, since `link` manages entries, not containers; then run `outfitter link --all`. Reference the migrated resources by slug from an agent's loadout like any protocol resource:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
<!-- ~/.agents/agents/daily/agent.md -->
|
|
82
|
+
---
|
|
83
|
+
name: daily
|
|
84
|
+
skills: [wiki, code-review] # formerly ~/.claude/skills/*
|
|
85
|
+
---
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`outfitter list` shows what resolved; `outfitter run daily --harness claude` launches through Outfitter; plain `claude` reads the links.
|
|
@@ -31,10 +31,8 @@ The committed `settings.yml` consumes shared catalogs **pinned to exact commits*
|
|
|
31
31
|
default_agent: founder
|
|
32
32
|
|
|
33
33
|
sources:
|
|
34
|
-
- github: ai-outfitter/.agent
|
|
35
|
-
ref: 2f9c1ab0d3e44b6f9d2c8a17e5b40c91d6f3a8e2
|
|
36
34
|
- github: ai-outfitter/community-profiles
|
|
37
|
-
ref:
|
|
35
|
+
ref: 32311cbf9eb17ae812c2ab5e91fa5f34d5946ca6 # v1.7.0
|
|
38
36
|
- path: . # this repository's own resources win last
|
|
39
37
|
```
|
|
40
38
|
|
|
@@ -47,9 +45,9 @@ When you are changing an upstream catalog itself, override its source in the git
|
|
|
47
45
|
```yaml
|
|
48
46
|
# settings.local.yml (gitignored — machine-specific absolute paths)
|
|
49
47
|
sources:
|
|
50
|
-
- path: /home/
|
|
51
|
-
- path: /home/
|
|
52
|
-
- path: /home/
|
|
48
|
+
- path: /home/developer/src/ai-outfitter/community-profiles
|
|
49
|
+
- path: /home/developer/src/ai-outfitter/actions
|
|
50
|
+
- path: /home/developer/.agents
|
|
53
51
|
```
|
|
54
52
|
|
|
55
53
|
Because `settings.local.yml` overlays its sibling with higher [precedence](./settings.md#precedence), your machine resolves live working trees while every other consumer of the repo keeps resolving the pinned SHAs. Worktrees keep an iteration branch isolated:
|
|
@@ -67,7 +65,7 @@ git worktree add ../worktrees/feat/sharper-review -b feat/sharper-review
|
|
|
67
65
|
4. Relaunch `outfitter` and test the behavior (a running session keeps the composition it started with).
|
|
68
66
|
5. Fold settled changes back to their home:
|
|
69
67
|
- personal → commit to your `.agents` repo;
|
|
70
|
-
- shared → commit in the upstream checkout, push, and open a PR against the catalog (`ai-outfitter/
|
|
68
|
+
- shared → commit in the upstream checkout, push, and open a PR against the catalog (`ai-outfitter/community-profiles`, `ai-outfitter/actions`, your org's `.agents`, …).
|
|
71
69
|
6. After the upstream PR merges: remove the local `path:` override, bump the pinned `ref:` in `settings.yml`, and `outfitter sync`.
|
|
72
70
|
|
|
73
71
|
## Consuming your repo from projects
|
|
@@ -45,4 +45,4 @@ The name is supported; the previous profile layout inside it is not.
|
|
|
45
45
|
|
|
46
46
|
## Claude Code users
|
|
47
47
|
|
|
48
|
-
If your pre-Outfitter configuration lives in `~/.claude` rather than `.outfitter/`, skip this page — use [
|
|
48
|
+
If your pre-Outfitter configuration lives in `~/.claude` rather than `.outfitter/`, skip this page — use [Linking into Claude Code and Codex](./linking-harnesses.md) instead.
|
|
@@ -27,8 +27,8 @@ isolation: inherit # inherit (default) or isolated; see below. Honored only from
|
|
|
27
27
|
|
|
28
28
|
# Where protocol resources come from, beyond this tree and ~/.agents.
|
|
29
29
|
sources:
|
|
30
|
-
- github: ai-outfitter
|
|
31
|
-
ref:
|
|
30
|
+
- github: ai-outfitter/community-profiles # owner/repo shorthand
|
|
31
|
+
ref: v1.7.0 # pin a commit, tag, or branch
|
|
32
32
|
# path: optional subdirectory containing the payload
|
|
33
33
|
- uri: git+https://git.example.com/team/agents.git
|
|
34
34
|
ref: v1.2.0
|
|
@@ -52,6 +52,22 @@ source_cache:
|
|
|
52
52
|
# Pseudonymous product analytics consent; defaults to true when absent.
|
|
53
53
|
telemetry:
|
|
54
54
|
enabled: false
|
|
55
|
+
|
|
56
|
+
# Additive loadout entries composed into every agent ahead of its own loadout.
|
|
57
|
+
agent_defaults:
|
|
58
|
+
extensions:
|
|
59
|
+
- git:github.com/ai-outfitter/pensieve@4b1e0d2c9a7f35e86b0d1c4a92f6e3d5a8b7c601
|
|
60
|
+
skills:
|
|
61
|
+
- organization-practices
|
|
62
|
+
mcp:
|
|
63
|
+
- github
|
|
64
|
+
append_system_prompt:
|
|
65
|
+
- file: prompts/organization.md
|
|
66
|
+
|
|
67
|
+
# Native harness settings shared by every agent run.
|
|
68
|
+
harness_defaults:
|
|
69
|
+
pi:
|
|
70
|
+
httpIdleTimeoutMs: 3600000
|
|
55
71
|
```
|
|
56
72
|
|
|
57
73
|
- `default_agent` / `default_harness` — which agent plain `outfitter` runs, and the harness it launches in.
|
|
@@ -66,6 +82,8 @@ telemetry:
|
|
|
66
82
|
accesses the network.
|
|
67
83
|
below its `repos/` directory.
|
|
68
84
|
- `telemetry.enabled` — the primary and sole persistent control for pseudonymous product analytics. Edit it directly to enable or disable telemetry. See [Telemetry](./telemetry.md) for consent precedence, automatic identifier cleanup, the event contract, and the current inert-build status.
|
|
85
|
+
- `agent_defaults` — additive loadout entries composed into **every** agent ahead of its own loadout; see [Agent defaults](#agent-defaults) below.
|
|
86
|
+
- `harness_defaults` — native Pi, Claude Code, or Codex settings applied to every run of that harness; see [Harness defaults](#harness-defaults) below.
|
|
69
87
|
|
|
70
88
|
## Precedence
|
|
71
89
|
|
|
@@ -78,4 +96,51 @@ Higher wins:
|
|
|
78
96
|
5. Cached remote settings (in configured order)
|
|
79
97
|
6. Built-in defaults
|
|
80
98
|
|
|
81
|
-
Scalar settings override. `sources` follows last-wins ordering per scope so a higher-precedence file replaces the complete lower-precedence list. `workflows`
|
|
99
|
+
Scalar settings override. `sources` follows last-wins ordering per scope so a higher-precedence file replaces the complete lower-precedence list. `workflows` and `agent_defaults` are additive ordered-set unions. `harness_defaults` deep-merges by harness, with higher-precedence leaves replacing lower-precedence leaves.
|
|
100
|
+
|
|
101
|
+
## Agent defaults
|
|
102
|
+
|
|
103
|
+
`agent_defaults` composes one set of additive loadout entries into every agent — local runs, Actions, and dumps alike — so an organization declares a shared extension, skill, MCP server, plugin, delegate, or appended prompt fragment once instead of duplicating it into every `agents/<id>/agent.md`:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
agent_defaults:
|
|
107
|
+
extensions:
|
|
108
|
+
- git:github.com/ai-outfitter/pensieve@4b1e0d2c9a7f35e86b0d1c4a92f6e3d5a8b7c601
|
|
109
|
+
skills:
|
|
110
|
+
- organization-practices
|
|
111
|
+
mcp:
|
|
112
|
+
- github
|
|
113
|
+
plugins:
|
|
114
|
+
- org-plugin
|
|
115
|
+
subagents:
|
|
116
|
+
- org-reviewer
|
|
117
|
+
append_system_prompt:
|
|
118
|
+
- file: prompts/organization.md # resolved like agent prompt sources: catalog `file`, active-project `repo_file`
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Composition rules:
|
|
122
|
+
|
|
123
|
+
- Defaults compose **before** each agent's own loadout — like a root-most ancestor ahead of the whole inheritance chain — using the same deterministic parent-first ordering and stable de-duplication as inherited agent loadouts. An agent that lists the same slug itself never duplicates it, and the settings layer wins first-encounter conflicts.
|
|
124
|
+
- Selections resolve catalog-wide across layers, never through an agent's local namespace.
|
|
125
|
+
- Only the additive loadout fields above are supported. Per-agent controls such as `model`, `thinking`, and `tools` stay agent-owned; `agents.md` remains shared prompt context, not a configuration manifest.
|
|
126
|
+
- `outfitter run`, `outfitter dump`, and `outfitter validate` compose the same effective defaults. Unresolved references are validation findings and composition warnings named `agent_defaults …`, and `outfitter dump` records the settings-layer provenance in `.outfitter/composition.json` plus a `settings.yml` carrying the merged defaults, so a dumped tree stays self-contained.
|
|
127
|
+
- Settings without `agent_defaults` behave exactly as before. The block is backend-neutral: no backend-specific keys, endpoints, or credentials.
|
|
128
|
+
|
|
129
|
+
## Harness defaults
|
|
130
|
+
|
|
131
|
+
`harness_defaults` keeps organization- or project-wide native coding-harness policy beside the portable agent catalog without putting harness-specific keys in every agent profile:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
harness_defaults:
|
|
135
|
+
pi:
|
|
136
|
+
httpIdleTimeoutMs: 3600000
|
|
137
|
+
claude:
|
|
138
|
+
includeCoAuthoredBy: false
|
|
139
|
+
codex:
|
|
140
|
+
features:
|
|
141
|
+
apps: false
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The keys below each harness are passed through as that harness's native settings. `outfitter run` merges Pi and Claude defaults into its temporary `settings.json`; a Pi profile's own configuration overlay remains higher precedence. Codex receives flattened `--config key=TOML` arguments. `outfitter link` manages the same values individually in Pi or Claude `settings.json` and Codex `config.toml`, leaving every unrelated native setting untouched. An unmanaged value is never adopted or overwritten.
|
|
145
|
+
|
|
146
|
+
Every loaded settings scope may contribute defaults. Objects deep-merge from low to high precedence, while arrays and scalar leaves replace. `outfitter dump` carries the effective block into the dumped tree. Unknown harness names are rejected; supported names are `pi`, `claude`, and `codex`.
|
|
@@ -246,6 +246,6 @@ state_persistence:
|
|
|
246
246
|
|
|
247
247
|
When a path uses `symlink`, the durable destination is the native CLI state location — `~/.pi/agent/...` for Pi, `~/.claude/...` for Claude Code. The native location is not another configuration layer: it does not participate in resolution or merge precedence; it only provides a durable destination for state paths.
|
|
248
248
|
|
|
249
|
-
For a [
|
|
249
|
+
For a [linked Claude Code home](./linking-harnesses.md), the managed `~/.claude` configuration entries are themselves symlinks into `~/.agents/`, so persisted configuration state lands in the protocol tree while session and auth state stays native.
|
|
250
250
|
|
|
251
251
|
For the complete adapter contract and rationale, see [State writeback strategy](../architecture/state_writeback_strategy.md).
|
|
@@ -45,11 +45,12 @@ Tasks and bake are not in this matrix — they are the subject of a [separate up
|
|
|
45
45
|
- **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.
|
|
46
46
|
- **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.
|
|
47
47
|
- **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.
|
|
48
|
+
- **Persistent links** — [`outfitter link`](./linking-harnesses.md) places managed skills, custom prompts (from commands), the shared-context `AGENTS.md`, and MCP servers registered through `codex mcp add` into `$CODEX_HOME` (default `~/.codex`), so plain `codex` sessions carry the composition without a launch. Agent identities are warned about and not linked, since Codex has no native agent definitions.
|
|
48
49
|
|
|
49
50
|
## Claude Code notes
|
|
50
51
|
|
|
51
52
|
- **Your configuration comes first** — by default a Claude run stands on the configuration already on the machine. Outfitter sets no `CLAUDE_CONFIG_DIR`; it declares the baked composition a Claude plugin and passes it through `--plugin-dir`, so the session keeps your workspace trust, `~/.claude/settings.json` permissions, credentials, plugins, and configured MCP servers, and the profile's skills, subagents, and prompts layer on top. Nothing is seeded and nothing is copied back, because Claude is reading and writing its real configuration directory throughout. Pass `--isolated`, or set `isolation: isolated` in your `~/.agents/settings.yml`, to launch from the composition alone — the reproducible form for CI and containers, and what the remaining bullets in this section describe. If the installed Claude is too old to load a plugin directory, Outfitter falls back to an isolated run and says so rather than failing the launch.
|
|
52
|
-
- **Isolated config and session state** — an isolated run 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),
|
|
53
|
+
- **Isolated config and session state** — an isolated run 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), MCP servers configured in `~/.claude` are not auto-discovered by Outfitter-launched Claude runs; those servers apply only when an agent selects them by slug. See the next bullet.
|
|
53
54
|
- **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.
|
|
54
55
|
- **MCP servers** — every Claude launch passes the generated `mcp.json` through `--mcp-config`. An inherited run stops there, so the composition's servers merge with the ones already configured on the machine: selecting a server says what the profile needs, not what the user may not have. An isolated run adds `--strict-mcp-config`, which excludes MCP servers from user or project configuration, `.claude.json`, and plugins so only the composition's servers are active.
|
|
55
56
|
- **Subagents** — selected `agents/<id>` definitions are materialized into the composition's agents directory. An inherited run loads them under the plugin's name (`<profile>:<subagent>`); an isolated run finds them natively under `CLAUDE_CONFIG_DIR`.
|
|
@@ -59,6 +60,7 @@ Tasks and bake are not in this matrix — they are the subject of a [separate up
|
|
|
59
60
|
- **Tool availability** — `tools.allow` (after `tools.deny` removes entries) maps to both `--tools` (_availability_: an unlisted builtin is not in the session) and `--allowedTools` (_permission_: the granted tools are pre-approved, so a headless session is not stopped by a prompt); `tools.deny` always maps to `--disallowedTools`, including when both are declared, and a bare denied name removes the tool from context per Claude's docs. An allowlist that `tools.deny` empties maps to `--tools ""`, Claude's documented "disable all tools" form. Caveat: per the CLI reference, `--tools` governs the built-in set only — MCP tools (`mcp__server__*`) are unaffected and are governed by which MCP servers the loadout selects, so `--tools ""` is not exactly pi's zero-tool session when MCP servers are present. Claude's behavior here comes from `claude --help` and the CLI reference, not local measurement.
|
|
60
61
|
- **DeepWork jobs** — job selection is Pi-only today and warns on Claude.
|
|
61
62
|
- **Bundled Outfitter skill** — every launch also publishes Outfitter's own self-documentation skill as a bundled plugin, so the agent can explain Outfitter and this launch's configuration.
|
|
63
|
+
- **Persistent links** — [`outfitter link`](./linking-harnesses.md) places managed skills, generated agent definitions, commands, the shared-context `CLAUDE.md`, and user-scope MCP servers into `$CLAUDE_CONFIG_DIR` (default `~/.claude`), so plain `claude` sessions carry the composition without a launch.
|
|
62
64
|
|
|
63
65
|
## Pi notes
|
|
64
66
|
|
|
@@ -6,7 +6,7 @@ This guide is for people who already use Pi, Claude Code, Codex, Cursor, or anot
|
|
|
6
6
|
|
|
7
7
|
**You already have a `.agents/` directory.** You're done with the hard part — Outfitter reads the protocol directly. Set `default_agent` in `.agents/settings.yml` to one of your [agent](./agents.md) slugs — the agent's own loadout selects its skills, subagents, and knowledge — and run `outfitter`. Nothing is converted or re-authored.
|
|
8
8
|
|
|
9
|
-
**Your setup lives in `~/.claude`.**
|
|
9
|
+
**Your setup lives in `~/.claude`.** Move it into `~/.agents/` and run `outfitter link` so Claude Code keeps working natively from the tree — see [Linking into Claude Code and Codex](./linking-harnesses.md). Your migrated skills, agents, and commands are then referenceable by slug like any protocol resource.
|
|
10
10
|
|
|
11
11
|
Starting from neither? `outfitter setup` bootstraps from the default catalog — see [Getting started](./getting-started.md).
|
|
12
12
|
|
|
@@ -40,7 +40,7 @@ The value is ambiguity reduction: every agent — and every skill that clones, o
|
|
|
40
40
|
|
|
41
41
|
## Reaching native harness runs
|
|
42
42
|
|
|
43
|
-
Composition only helps runs that go through it — the rule should also reach a bare `claude` session that never touches Outfitter.
|
|
43
|
+
Composition only helps runs that go through it — the rule should also reach a bare `claude` session that never touches Outfitter. [`outfitter link`](../linking-harnesses.md) symlinks `~/.claude/CLAUDE.md` (and `~/.codex/AGENTS.md`) to the winning `~/.agents/agents.md`, so native Claude Code and Codex read the protocol tree and editing the tree edits what they see. This is the persistent projection [#187](https://github.com/ai-outfitter/outfitter/issues/187) deferred; setup still creates no links.
|
|
44
44
|
|
|
45
45
|
## Payoff
|
|
46
46
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-outfitter/outfitter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.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": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"cross-spawn": "^7.0.6",
|
|
48
48
|
"liquidjs": "^10.27.0",
|
|
49
49
|
"posthog-node": "^5.49.1",
|
|
50
|
+
"smol-toml": "^1.8.0",
|
|
50
51
|
"yaml": "^2.9.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
@@ -103,7 +103,54 @@
|
|
|
103
103
|
"custom_settings": {
|
|
104
104
|
"type": "object",
|
|
105
105
|
"description": "Arbitrary YAML-compatible values exposed to Outfitter composition templates as outfitter.custom_settings."
|
|
106
|
+
},
|
|
107
|
+
"agent_defaults": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"description": "Additive loadout entries composed into every agent before its own loadout, using the same deterministic ordering and stable de-duplication as inherited agent loadouts.",
|
|
110
|
+
"properties": {
|
|
111
|
+
"extensions": { "$ref": "#/$defs/slugList" },
|
|
112
|
+
"skills": { "$ref": "#/$defs/slugList" },
|
|
113
|
+
"mcp": { "$ref": "#/$defs/slugList" },
|
|
114
|
+
"plugins": { "$ref": "#/$defs/slugList" },
|
|
115
|
+
"subagents": { "$ref": "#/$defs/slugList" },
|
|
116
|
+
"append_system_prompt": {
|
|
117
|
+
"oneOf": [
|
|
118
|
+
{ "$ref": "#/$defs/promptSource" },
|
|
119
|
+
{ "type": "array", "items": { "$ref": "#/$defs/promptSource" } }
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"additionalProperties": false
|
|
124
|
+
},
|
|
125
|
+
"harness_defaults": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"description": "Harness-native settings applied to every composed agent and persistently reconciled by outfitter link.",
|
|
128
|
+
"properties": {
|
|
129
|
+
"pi": { "$ref": "#/$defs/nativeSettings" },
|
|
130
|
+
"claude": { "$ref": "#/$defs/nativeSettings" },
|
|
131
|
+
"codex": { "$ref": "#/$defs/nativeSettings" }
|
|
132
|
+
},
|
|
133
|
+
"additionalProperties": false
|
|
106
134
|
}
|
|
107
135
|
},
|
|
108
|
-
"additionalProperties": true
|
|
136
|
+
"additionalProperties": true,
|
|
137
|
+
"$defs": {
|
|
138
|
+
"slugList": {
|
|
139
|
+
"type": "array",
|
|
140
|
+
"items": { "type": "string", "minLength": 1 }
|
|
141
|
+
},
|
|
142
|
+
"promptSource": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"oneOf": [{ "required": ["file"] }, { "required": ["repo_file"] }],
|
|
145
|
+
"properties": {
|
|
146
|
+
"file": { "type": "string", "minLength": 1 },
|
|
147
|
+
"repo_file": { "type": "string", "minLength": 1 }
|
|
148
|
+
},
|
|
149
|
+
"additionalProperties": false
|
|
150
|
+
},
|
|
151
|
+
"nativeSettings": {
|
|
152
|
+
"type": "object",
|
|
153
|
+
"description": "A harness-native settings document. Native keys are validated by the selected harness release."
|
|
154
|
+
}
|
|
155
|
+
}
|
|
109
156
|
}
|