@ai-outfitter/outfitter 0.7.2 → 0.9.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 +12 -2
- package/code/pi-extension/src/outfitter-extension.js +720 -0
- package/dist/agents/AdapterStatePaths.js +3 -1
- package/dist/agents/AdapterStatePaths.js.map +1 -1
- package/dist/agents/AgentAdapter.d.ts +3 -0
- package/dist/agents/AgentLaunch.js +5 -0
- package/dist/agents/AgentLaunch.js.map +1 -1
- package/dist/agents/OutfitterDocs.d.ts +2 -0
- package/dist/agents/OutfitterDocs.js +38 -0
- package/dist/agents/OutfitterDocs.js.map +1 -0
- package/dist/agents/claude/ClaudeAdapter.js +3 -2
- package/dist/agents/claude/ClaudeAdapter.js.map +1 -1
- package/dist/agents/pi/PiAdapter.js +24 -19
- package/dist/agents/pi/PiAdapter.js.map +1 -1
- package/dist/agents/pi/PiExtensionCache.d.ts +8 -0
- package/dist/agents/pi/PiExtensionCache.js +95 -5
- package/dist/agents/pi/PiExtensionCache.js.map +1 -1
- package/dist/agents/pi/PiSkillSources.d.ts +8 -0
- package/dist/agents/pi/PiSkillSources.js +69 -0
- package/dist/agents/pi/PiSkillSources.js.map +1 -0
- package/dist/cli/OutfitterCli.js +7 -2
- package/dist/cli/OutfitterCli.js.map +1 -1
- package/dist/cli/commands/PiLoginLaunch.js +28 -737
- package/dist/cli/commands/PiLoginLaunch.js.map +1 -1
- package/dist/cli/commands/RunCommand.d.ts +6 -17
- package/dist/cli/commands/RunCommand.js +115 -186
- package/dist/cli/commands/RunCommand.js.map +1 -1
- package/dist/cli/commands/SetupCommand.d.ts +4 -63
- package/dist/cli/commands/SetupCommand.js +13 -673
- package/dist/cli/commands/SetupCommand.js.map +1 -1
- package/dist/cli/commands/SyncCommand.d.ts +1 -1
- package/dist/cli/commands/SyncCommand.js +37 -12
- package/dist/cli/commands/SyncCommand.js.map +1 -1
- package/dist/cli/commands/run/RunFirstRunOnboarding.d.ts +7 -0
- package/dist/cli/commands/run/RunFirstRunOnboarding.js +52 -0
- package/dist/cli/commands/run/RunFirstRunOnboarding.js.map +1 -0
- package/dist/cli/commands/run/RunLaunchSummary.d.ts +2 -0
- package/dist/cli/commands/run/RunLaunchSummary.js +35 -0
- package/dist/cli/commands/run/RunLaunchSummary.js.map +1 -0
- package/dist/cli/commands/run/RunProfileResolution.d.ts +37 -0
- package/dist/cli/commands/run/RunProfileResolution.js +124 -0
- package/dist/cli/commands/run/RunProfileResolution.js.map +1 -0
- package/dist/cli/commands/run/RunStateWritePrompt.d.ts +2 -0
- package/dist/cli/commands/run/RunStateWritePrompt.js +29 -0
- package/dist/cli/commands/run/RunStateWritePrompt.js.map +1 -0
- package/dist/cli/commands/setup/SetupPrompts.d.ts +14 -0
- package/dist/cli/commands/setup/SetupPrompts.js +296 -0
- package/dist/cli/commands/setup/SetupPrompts.js.map +1 -0
- package/dist/cli/commands/setup/SetupSourceImport.d.ts +5 -0
- package/dist/cli/commands/setup/SetupSourceImport.js +177 -0
- package/dist/cli/commands/setup/SetupSourceImport.js.map +1 -0
- package/dist/cli/commands/setup/SetupSourceLaunch.d.ts +4 -0
- package/dist/cli/commands/setup/SetupSourceLaunch.js +65 -0
- package/dist/cli/commands/setup/SetupSourceLaunch.js.map +1 -0
- package/dist/cli/commands/setup/SetupStarterSource.d.ts +21 -0
- package/dist/cli/commands/setup/SetupStarterSource.js +133 -0
- package/dist/cli/commands/setup/SetupStarterSource.js.map +1 -0
- package/dist/cli/commands/setup/SetupTypes.d.ts +91 -0
- package/dist/cli/commands/setup/SetupTypes.js +26 -0
- package/dist/cli/commands/setup/SetupTypes.js.map +1 -0
- package/dist/compositeProfile/CompositeProfileCleanup.d.ts +9 -0
- package/dist/compositeProfile/CompositeProfileCleanup.js +87 -0
- package/dist/compositeProfile/CompositeProfileCleanup.js.map +1 -0
- package/dist/compositeProfile/StatePersistence.d.ts +16 -1
- package/dist/compositeProfile/StatePersistence.js +27 -5
- package/dist/compositeProfile/StatePersistence.js.map +1 -1
- package/dist/fs/SafeSymlink.d.ts +13 -0
- package/dist/fs/SafeSymlink.js +50 -0
- package/dist/fs/SafeSymlink.js.map +1 -0
- package/dist/settings/SettingsLoader.d.ts +1 -0
- package/dist/settings/SettingsLoader.js +10 -1
- package/dist/settings/SettingsLoader.js.map +1 -1
- package/doc/architecture/state_writeback_strategy.md +350 -0
- package/doc/documentation/README.md +21 -0
- package/doc/documentation/cli.md +74 -0
- package/doc/documentation/concepts.md +54 -0
- package/doc/documentation/first-time-cli-agent-users.md +137 -0
- package/doc/documentation/getting-started.md +49 -0
- package/doc/documentation/iterating-on-profiles.md +109 -0
- package/doc/documentation/profile-repository.md +111 -0
- package/doc/documentation/profiles.md +183 -0
- package/doc/documentation/state.md +224 -0
- package/doc/documentation/support-matrix.md +46 -0
- package/doc/documentation/switching-to-outfitter.md +130 -0
- package/doc/documentation/usecases/engineering.md +114 -0
- package/doc/documentation/usecases/organization-profile-catalog.md +140 -0
- package/doc/documentation/usecases/persona-reviews.md +173 -0
- package/doc/philosophy.md +25 -0
- package/package.json +3 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Creates symlinks with a Windows-only fallback for environments that deny symlink creation.
|
|
2
|
+
import { copyFileSync, cpSync, symlinkSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
// Windows without Developer Mode rejects symlink creation with EPERM. Directories fall
|
|
5
|
+
// back to junctions, which need no privilege but require an absolute target; files (and
|
|
6
|
+
// directories whose junction also fails) fall back to a one-way copy with a warning
|
|
7
|
+
// because agent writes to the copy cannot persist back to the source. On POSIX platforms
|
|
8
|
+
// a permission error is a real filesystem problem, so it is rethrown instead of being
|
|
9
|
+
// silently downgraded to a copy.
|
|
10
|
+
export const createSafeSymlink = (input, dependencies = {}) => {
|
|
11
|
+
const symlink = dependencies.symlink ?? symlinkSync;
|
|
12
|
+
/* v8 ignore next -- the process platform default is direct runtime behavior; tests inject a platform. */
|
|
13
|
+
const platform = dependencies.platform ?? process.platform;
|
|
14
|
+
try {
|
|
15
|
+
symlink(input.sourcePath, input.outputPath, input.directory ? 'dir' : 'file');
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (platform !== 'win32' || !isSymlinkPermissionError(error)) {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
createWindowsSafeSymlinkFallback(symlink, input, dependencies.warn);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const createWindowsSafeSymlinkFallback = (symlink, input, warn) => {
|
|
25
|
+
if (input.directory) {
|
|
26
|
+
try {
|
|
27
|
+
symlink(resolve(input.sourcePath), input.outputPath, 'junction');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (!isSymlinkPermissionError(error)) {
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
copySafeSymlinkFallback(input, warn);
|
|
37
|
+
};
|
|
38
|
+
const copySafeSymlinkFallback = (input, warn) => {
|
|
39
|
+
if (input.directory) {
|
|
40
|
+
cpSync(input.sourcePath, input.outputPath, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
copyFileSync(input.sourcePath, input.outputPath);
|
|
44
|
+
}
|
|
45
|
+
/* v8 ignore next -- console fallback is direct CLI behavior; tests inject a warn writer. */
|
|
46
|
+
(warn ?? console.error)(`${input.label} could not be symlinked (symlinks are unavailable on this platform); ` +
|
|
47
|
+
`copied '${input.sourcePath}' instead, so writes to it will not persist back.`);
|
|
48
|
+
};
|
|
49
|
+
const isSymlinkPermissionError = (error) => error instanceof Error && 'code' in error && (error.code === 'EPERM' || error.code === 'EACCES');
|
|
50
|
+
//# sourceMappingURL=SafeSymlink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SafeSymlink.js","sourceRoot":"","sources":["../../src/fs/SafeSymlink.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,uFAAuF;AACvF,wFAAwF;AACxF,oFAAoF;AACpF,yFAAyF;AACzF,sFAAsF;AACtF,iCAAiC;AACjC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAuB,EAAE,eAAwC,EAAE,EAAQ,EAAE;IAC7G,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,IAAI,WAAW,CAAC;IACpD,yGAAyG;IACzG,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IAE3D,IAAI,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAChF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7D,MAAM,KAAK,CAAC;QACd,CAAC;QAED,gCAAgC,CAAC,OAAO,EAAE,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACtE,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,gCAAgC,GAAG,CACvC,OAA2B,EAC3B,KAAuB,EACvB,IAA6C,EACvC,EAAE;IACR,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACjE,OAAO;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrC,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAED,uBAAuB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,uBAAuB,GAAG,CAAC,KAAuB,EAAE,IAA6C,EAAQ,EAAE;IAC/G,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QACpB,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACnD,CAAC;IAED,4FAA4F;IAC5F,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CACrB,GAAG,KAAK,CAAC,KAAK,uEAAuE;QACnF,WAAW,KAAK,CAAC,UAAU,mDAAmD,CACjF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,wBAAwB,GAAG,CAAC,KAAc,EAAW,EAAE,CAC3D,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC"}
|
|
@@ -28,6 +28,7 @@ export interface SettingsDiscoveryInput {
|
|
|
28
28
|
export declare const createSettingsLoadPlan: (locations: readonly SettingsLocation[]) => SettingsLoadPlan;
|
|
29
29
|
export declare const discoverSettingsLoadPlan: (input: SettingsDiscoveryInput) => SettingsLoadPlan;
|
|
30
30
|
export declare const discoverRemoteSettingsLoadPlan: (homeDirectory: string, remoteSettings: readonly RemoteSettingsReference[]) => SettingsLoadPlan;
|
|
31
|
+
export declare const resolveCachedRemoteSettingsPath: (homeDirectory: string, source: RemoteSettingsReference) => string;
|
|
31
32
|
export declare const loadSettingsFiles: (plan: SettingsLoadPlan) => SettingsLoadResult;
|
|
32
33
|
export declare const loadSettings: (plan: SettingsLoadPlan) => LoadedSettings;
|
|
33
34
|
export declare const loadSettingsWithCachedRemoteSettings: (input: SettingsDiscoveryInput, remoteSettingsReferencesOverride?: readonly RemoteSettingsReference[]) => LoadedSettings;
|
|
@@ -14,6 +14,15 @@ export const discoverSettingsLoadPlan = (input) => createSettingsLoadPlan([
|
|
|
14
14
|
{ scope: 'project-local', path: join(input.projectDirectory, '.outfitter', 'local', 'settings.yml') },
|
|
15
15
|
]);
|
|
16
16
|
export const discoverRemoteSettingsLoadPlan = (homeDirectory, remoteSettings) => discoverRemoteSettingsLocations(homeDirectory, remoteSettings).plan;
|
|
17
|
+
export const resolveCachedRemoteSettingsPath = (homeDirectory, source) => {
|
|
18
|
+
const repositoryPath = createRemoteRepositoryCachePath(homeDirectory, source);
|
|
19
|
+
const configuredPath = resolveRemoteRepositorySubpath(repositoryPath, source.path);
|
|
20
|
+
if (existsSync(configuredPath) || source.path !== 'settings.yml') {
|
|
21
|
+
return configuredPath;
|
|
22
|
+
}
|
|
23
|
+
const nestedOutfitterSettingsPath = resolveRemoteRepositorySubpath(repositoryPath, '.outfitter/settings.yml');
|
|
24
|
+
return existsSync(nestedOutfitterSettingsPath) ? nestedOutfitterSettingsPath : configuredPath;
|
|
25
|
+
};
|
|
17
26
|
export const loadSettingsFiles = (plan) => {
|
|
18
27
|
const files = [];
|
|
19
28
|
const issues = [];
|
|
@@ -54,7 +63,7 @@ const discoverRemoteSettingsLocations = (homeDirectory, remoteSettings) => {
|
|
|
54
63
|
try {
|
|
55
64
|
locations.push({
|
|
56
65
|
scope: 'remote',
|
|
57
|
-
path:
|
|
66
|
+
path: resolveCachedRemoteSettingsPath(homeDirectory, source),
|
|
58
67
|
});
|
|
59
68
|
}
|
|
60
69
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsLoader.js","sourceRoot":"","sources":["../../src/settings/SettingsLoader.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SettingsLoader.js","sourceRoot":"","sources":["../../src/settings/SettingsLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# State Writeback Strategy
|
|
2
|
+
|
|
3
|
+
This document describes Outfitter's current model for handling writes that agent CLIs make inside a composite profile.
|
|
4
|
+
|
|
5
|
+
A composite profile is temporary, but agent CLIs sometimes perform intentionally durable writes, such as logging in, installing plugins, changing settings, or updating MCP configuration.
|
|
6
|
+
Outfitter makes those paths explicit: adapter-declared writable paths are materialized with a resolved `state_persistence` strategy before the child CLI starts, and non-persistent or unknown writes are diagnosed after the child exits.
|
|
7
|
+
|
|
8
|
+
## Functional model
|
|
9
|
+
|
|
10
|
+
Outfitter separates three kinds of files that may exist in a composite profile:
|
|
11
|
+
|
|
12
|
+
1. **Generated runtime files**: files Outfitter assembles from settings, profiles, templates, and adapter rules.
|
|
13
|
+
Outfitter may regenerate these while the child agent is running when their source inputs change.
|
|
14
|
+
2. **Declared state paths**: adapter-known files or directories the agent CLI may update intentionally, such as auth, settings, MCP config, plugins, caches, and sessions.
|
|
15
|
+
3. **Unknown writes**: files or directories the agent creates outside the adapter-declared state paths.
|
|
16
|
+
|
|
17
|
+
Only declared state paths can be made durable automatically.
|
|
18
|
+
Unknown writes are never silently persisted because Outfitter does not know their intended owner, merge rules, or durable destination.
|
|
19
|
+
Generated runtime files and declared state paths are deliberately handled separately so live profile/template updates do not erase or re-baseline agent state changes made during the same run.
|
|
20
|
+
|
|
21
|
+
The user-facing state update lifecycle is:
|
|
22
|
+
|
|
23
|
+
1. **Choose a profile**.
|
|
24
|
+
Profile resolution determines the effective `state_persistence` map using normal profile precedence.
|
|
25
|
+
2. **Resolve adapter defaults**.
|
|
26
|
+
For each path the selected adapter declares, Outfitter uses the profile override when present and otherwise uses the adapter default.
|
|
27
|
+
3. **Prepare the composite profile**.
|
|
28
|
+
Durable paths are connected to a profile-managed or native CLI location; non-durable paths are created as normal temporary composite profile paths.
|
|
29
|
+
4. **Run the agent**.
|
|
30
|
+
The agent CLI reads and writes the composite profile as if it were its normal configuration directory.
|
|
31
|
+
5. **Classify changes after exit**.
|
|
32
|
+
Outfitter checks non-durable declared paths and unknown paths and reports or fails according to their strategies.
|
|
33
|
+
6. **Clean up temporary state**.
|
|
34
|
+
Temporary composite profile contents are discarded; durable symlink targets remain in their profile or native CLI location.
|
|
35
|
+
|
|
36
|
+
## Current behavior
|
|
37
|
+
|
|
38
|
+
- Composite profiles remain temporary and reproducible by default.
|
|
39
|
+
- Outfitter does not do generic post-run copy-back or JSON/YAML merge-back.
|
|
40
|
+
- Persistent state is represented by symlinking a composite profile path to a profile file/directory or to the native CLI fallback path.
|
|
41
|
+
- Adapters may generate a concrete runtime file for a declared state path when they need deterministic launch-time reconciliation.
|
|
42
|
+
For example, the Pi adapter can generate a transformed `settings.json` that removes native `packages` entries already supplied by profile-controlled extensions, and then mark that declared path as `discard` for write detection during the run.
|
|
43
|
+
- Every adapter-declared state path has a resolved strategy before launch: profile overrides win, otherwise the adapter `default_strategy` is used, except for adapter-generated reconciliation files that are intentionally treated as discarded runtime files.
|
|
44
|
+
- Invalid or disallowed profile-requested `state_persistence` strategies fail before launch; adapter-internal reconciliation may still choose a one-run handling strategy for a generated runtime file.
|
|
45
|
+
- Non-persistent `warn` and `error` strategies are checked after the child CLI exits.
|
|
46
|
+
- Unknown writes outside adapter-declared paths are checked with the adapter's `unknown` pseudo-path strategy.
|
|
47
|
+
- `prompt` is reserved for a future interactive/control-plane workflow.
|
|
48
|
+
When accepted by a declaration today, it is treated as a non-persistent diagnostic like `warn`.
|
|
49
|
+
|
|
50
|
+
## Non-goals
|
|
51
|
+
|
|
52
|
+
- Outfitter does not implement generic copy-back from the composite profile to profiles.
|
|
53
|
+
- Outfitter does not implement generic structured merge-back.
|
|
54
|
+
- Outfitter does not silently persist unknown writes.
|
|
55
|
+
|
|
56
|
+
## Profile stack and native fallback
|
|
57
|
+
|
|
58
|
+
State persistence is a normal profile setting.
|
|
59
|
+
Its strategy overrides resolve through the same selected-profile stack as other profile data. When the user provides `--profile X`, only definitions of `X` and its explicit inheritance chain participate; the configured `default_profile` is selected only when no explicit profile is provided.
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
project-local definition of selected profile
|
|
63
|
+
project definition of selected profile
|
|
64
|
+
user definition of selected profile
|
|
65
|
+
URI/cache definitions of selected profile
|
|
66
|
+
explicit inheritance
|
|
67
|
+
Outfitter defaults
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Native CLI state is not represented as an extra profile layer.
|
|
71
|
+
For `symlink` paths without a profile-provided source, 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/`.
|
|
72
|
+
This native fallback is not a base profile: it does not participate in profile inheritance or merge precedence, and it cannot contribute controls or profile YAML.
|
|
73
|
+
Claude Code `projects/` is additionally controlled by `controls.session_directory` or `controls.claude.session_directory` when set.
|
|
74
|
+
|
|
75
|
+
## Path-keyed adapter declarations
|
|
76
|
+
|
|
77
|
+
Adapters declare writable state paths directly, using relative file paths as keys.
|
|
78
|
+
Directory paths use a trailing slash.
|
|
79
|
+
The same key is used for adapter coverage, `state_persistence` overrides, profile resource lookup, native fallback lookup, and composite profile materialization.
|
|
80
|
+
|
|
81
|
+
The Pi adapter currently declares:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
state_paths:
|
|
85
|
+
auth.json:
|
|
86
|
+
default_strategy: symlink
|
|
87
|
+
allowed_strategies: [symlink, error, prompt]
|
|
88
|
+
|
|
89
|
+
settings.json:
|
|
90
|
+
default_strategy: symlink
|
|
91
|
+
allowed_strategies: [symlink, warn, error, prompt]
|
|
92
|
+
note: >-
|
|
93
|
+
When profile-controlled Pi extensions duplicate native settings packages,
|
|
94
|
+
Outfitter may generate a transformed runtime settings.json and treat this
|
|
95
|
+
declared path as discard for that launch. That discard handling is
|
|
96
|
+
adapter-internal; users still cannot request settings.json: discard
|
|
97
|
+
because discard is not listed in allowed_strategies.
|
|
98
|
+
|
|
99
|
+
keybindings.json:
|
|
100
|
+
default_strategy: symlink
|
|
101
|
+
allowed_strategies: [symlink, warn, error, prompt]
|
|
102
|
+
note: >-
|
|
103
|
+
Outfitter generates a transformed runtime keybindings.json so Shift+Tab
|
|
104
|
+
can switch Outfitter modes and Ctrl+Shift+T can cycle Pi thinking levels.
|
|
105
|
+
The generated runtime file is treated as discard for that launch.
|
|
106
|
+
|
|
107
|
+
mcp.json:
|
|
108
|
+
default_strategy: symlink
|
|
109
|
+
allowed_strategies: [symlink, warn, error, prompt]
|
|
110
|
+
|
|
111
|
+
models.json:
|
|
112
|
+
default_strategy: symlink
|
|
113
|
+
allowed_strategies: [symlink, warn, error, prompt]
|
|
114
|
+
|
|
115
|
+
trust.json:
|
|
116
|
+
default_strategy: symlink
|
|
117
|
+
allowed_strategies: [symlink, warn, error, prompt]
|
|
118
|
+
|
|
119
|
+
plugins/:
|
|
120
|
+
default_strategy: symlink
|
|
121
|
+
allowed_strategies: [symlink, discard, warn, error, prompt]
|
|
122
|
+
|
|
123
|
+
cache/:
|
|
124
|
+
default_strategy: symlink
|
|
125
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
126
|
+
|
|
127
|
+
sessions/:
|
|
128
|
+
default_strategy: symlink
|
|
129
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
130
|
+
|
|
131
|
+
npm/:
|
|
132
|
+
default_strategy: symlink
|
|
133
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
134
|
+
|
|
135
|
+
git/:
|
|
136
|
+
default_strategy: symlink
|
|
137
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
138
|
+
|
|
139
|
+
tmp/:
|
|
140
|
+
default_strategy: symlink
|
|
141
|
+
allowed_strategies: [symlink, discard]
|
|
142
|
+
|
|
143
|
+
utilities/:
|
|
144
|
+
default_strategy: symlink
|
|
145
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
146
|
+
|
|
147
|
+
bin/:
|
|
148
|
+
default_strategy: symlink
|
|
149
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
150
|
+
|
|
151
|
+
unknown:
|
|
152
|
+
default_strategy: warn
|
|
153
|
+
allowed_strategies: [discard, warn, error, prompt]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The Claude Code adapter currently declares:
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
state_paths:
|
|
160
|
+
settings.json:
|
|
161
|
+
default_strategy: symlink
|
|
162
|
+
allowed_strategies: [symlink, warn, error, prompt]
|
|
163
|
+
|
|
164
|
+
agents/:
|
|
165
|
+
default_strategy: symlink
|
|
166
|
+
allowed_strategies: [symlink, discard, warn, error, prompt]
|
|
167
|
+
|
|
168
|
+
skills/:
|
|
169
|
+
default_strategy: symlink
|
|
170
|
+
allowed_strategies: [symlink, discard, warn, error, prompt]
|
|
171
|
+
|
|
172
|
+
commands/:
|
|
173
|
+
default_strategy: symlink
|
|
174
|
+
allowed_strategies: [symlink, discard, warn, error, prompt]
|
|
175
|
+
|
|
176
|
+
plugins/:
|
|
177
|
+
default_strategy: symlink
|
|
178
|
+
allowed_strategies: [symlink, discard, warn, error, prompt]
|
|
179
|
+
|
|
180
|
+
projects/:
|
|
181
|
+
default_strategy: symlink
|
|
182
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
183
|
+
|
|
184
|
+
debug/:
|
|
185
|
+
default_strategy: symlink
|
|
186
|
+
allowed_strategies: [symlink, discard, warn, error]
|
|
187
|
+
|
|
188
|
+
unknown:
|
|
189
|
+
default_strategy: warn
|
|
190
|
+
allowed_strategies: [discard, warn, error, prompt]
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Profile layout for state files
|
|
194
|
+
|
|
195
|
+
State files live under the relevant CLI-specific profile folder:
|
|
196
|
+
|
|
197
|
+
```text
|
|
198
|
+
profiles/
|
|
199
|
+
default/
|
|
200
|
+
profile.yml
|
|
201
|
+
cli_specific/
|
|
202
|
+
pi/
|
|
203
|
+
auth.json
|
|
204
|
+
settings.json
|
|
205
|
+
keybindings.json
|
|
206
|
+
plugins/
|
|
207
|
+
claude/
|
|
208
|
+
settings.json
|
|
209
|
+
skills/
|
|
210
|
+
commands/
|
|
211
|
+
plugins/
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Except for special adapter paths described below, when a selected strategy is `symlink`, Outfitter searches the resolved profile folders from highest to lowest precedence for `cli_specific/<adapter>/<state-path>`.
|
|
215
|
+
If a profile contains the file or directory, Outfitter symlinks the composite profile path to that source.
|
|
216
|
+
|
|
217
|
+
For most Pi paths, if no profile source exists, Outfitter falls back to the corresponding native Pi agent path under `~/.pi/agent`.
|
|
218
|
+
Missing native fallback files/directories are created so the composite profile symlink has a durable destination.
|
|
219
|
+
|
|
220
|
+
Pi `utilities/` and `bin/` are special cache-backed paths: both resolve to `<cache_directory>/utilities` instead of profile or native Pi state.
|
|
221
|
+
This keeps pi-managed helper binaries reusable across temporary composite profiles without treating them as user-editable profile files.
|
|
222
|
+
|
|
223
|
+
For most Claude Code paths, if no profile source exists, Outfitter falls back to the corresponding native Claude Code path under `~/.claude`.
|
|
224
|
+
Claude Code `projects/` is special: `controls.claude.session_directory` overrides generic `controls.session_directory`, and the selected session directory becomes the `projects/` symlink source.
|
|
225
|
+
If neither session-directory control is present, `projects/` falls back to `~/.claude/projects`.
|
|
226
|
+
|
|
227
|
+
## `state_persistence`
|
|
228
|
+
|
|
229
|
+
Profiles may override persistence by mapping adapter-declared paths to strategy names:
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
state_persistence:
|
|
233
|
+
auth.json: symlink
|
|
234
|
+
settings.json: symlink
|
|
235
|
+
plugins/: symlink
|
|
236
|
+
cache/: discard
|
|
237
|
+
sessions/: discard
|
|
238
|
+
unknown: warn
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The values are concrete strategy names.
|
|
242
|
+
`state_persistence` only needs overrides; omitted paths use the adapter declaration's `default_strategy`.
|
|
243
|
+
|
|
244
|
+
`state_persistence` is validated by the profile JSON Schema at read boundaries.
|
|
245
|
+
Outfitter also validates the resolved strategy against the adapter declaration before launch.
|
|
246
|
+
|
|
247
|
+
Functional examples:
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
# Persist logins and settings, but make caches and sessions run-local.
|
|
251
|
+
state_persistence:
|
|
252
|
+
auth.json: symlink
|
|
253
|
+
settings.json: symlink
|
|
254
|
+
cache/: discard
|
|
255
|
+
sessions/: discard
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```yaml
|
|
259
|
+
# CI profile: fail if pi changes settings, MCP config, or unknown files.
|
|
260
|
+
state_persistence:
|
|
261
|
+
settings.json: error
|
|
262
|
+
mcp.json: error
|
|
263
|
+
plugins/: error
|
|
264
|
+
unknown: error
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```yaml
|
|
268
|
+
# Exploratory profile: allow plugin experiments but report them after exit.
|
|
269
|
+
state_persistence:
|
|
270
|
+
plugins/: warn
|
|
271
|
+
unknown: warn
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Composite profile materialization
|
|
275
|
+
|
|
276
|
+
Before launch, Outfitter processes each adapter-declared state path:
|
|
277
|
+
|
|
278
|
+
1. Resolve the path's strategy from profile `state_persistence` overrides, then the adapter `default_strategy`.
|
|
279
|
+
2. Validate that the strategy is allowed for that path.
|
|
280
|
+
3. Resolve a source path through the profile hierarchy when the strategy is `symlink`.
|
|
281
|
+
4. Materialize the composite profile path.
|
|
282
|
+
5. Record a baseline fingerprint for non-persistent and unknown write detection.
|
|
283
|
+
|
|
284
|
+
For `symlink`, Outfitter creates a symlink from the composite profile path to the resolved profile or native CLI source.
|
|
285
|
+
|
|
286
|
+
For `discard`, `warn`, `error`, and `prompt`, Outfitter creates normal temporary composite profile paths where needed and observes whether they changed.
|
|
287
|
+
|
|
288
|
+
## Unknown writes
|
|
289
|
+
|
|
290
|
+
The `unknown` pseudo-path controls writes outside adapter-declared paths:
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
state_persistence:
|
|
294
|
+
unknown: warn
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Supported `unknown` strategies are non-persistent only:
|
|
298
|
+
|
|
299
|
+
- `discard`
|
|
300
|
+
- `warn`
|
|
301
|
+
- `error`
|
|
302
|
+
- `prompt`
|
|
303
|
+
|
|
304
|
+
`unknown` does not support `symlink`, because there is no declared durable destination.
|
|
305
|
+
|
|
306
|
+
## Strategy selection guide
|
|
307
|
+
|
|
308
|
+
Use `symlink` when a write is part of durable agent setup, such as logging in, editing native settings, updating MCP config, or installing plugins that should be reused.
|
|
309
|
+
Use `discard` when the data is useful only during the current run, such as cache entries or throwaway sessions.
|
|
310
|
+
Use `warn` when mutation is acceptable but should be visible to the user.
|
|
311
|
+
Use `error` when mutation means the run was not reproducible enough, especially in CI or locked-down project profiles.
|
|
312
|
+
Use `prompt` only as a forward-compatible declaration for future interactive handling.
|
|
313
|
+
|
|
314
|
+
## Strategies
|
|
315
|
+
|
|
316
|
+
### `symlink`
|
|
317
|
+
|
|
318
|
+
Outfitter resolves the state path through the profile hierarchy, then the native CLI fallback, and symlinks that source into the composite profile.
|
|
319
|
+
Persistence happens because the CLI writes through the symlink to an intentional file or directory.
|
|
320
|
+
|
|
321
|
+
### `discard`
|
|
322
|
+
|
|
323
|
+
Writes are allowed in the composite profile and are thrown away when the composite profile is deleted.
|
|
324
|
+
Outfitter does not emit diagnostics for changed `discard` paths.
|
|
325
|
+
|
|
326
|
+
### `warn`
|
|
327
|
+
|
|
328
|
+
Writes are allowed, discarded, and reported after the child exits.
|
|
329
|
+
`--strict` makes these warnings fatal.
|
|
330
|
+
|
|
331
|
+
### `error`
|
|
332
|
+
|
|
333
|
+
Writes are allowed during the child process but cause Outfitter to fail after the child exits if the path changed.
|
|
334
|
+
This is useful for CI and strict reproducibility.
|
|
335
|
+
|
|
336
|
+
### `prompt`
|
|
337
|
+
|
|
338
|
+
`prompt` is reserved for a future interactive/control-plane workflow.
|
|
339
|
+
Current implementations that allow it treat writes as non-persistent diagnostics, equivalent to `warn`, with the strategy name preserved in the message.
|
|
340
|
+
|
|
341
|
+
## Rationale
|
|
342
|
+
|
|
343
|
+
Path-keyed state declarations keep the model simple:
|
|
344
|
+
|
|
345
|
+
- the adapter declares the paths it knows the CLI may write and their default strategies;
|
|
346
|
+
- profiles may provide files at those same paths;
|
|
347
|
+
- the native fallback exposes native CLI files at those same paths;
|
|
348
|
+
- `state_persistence` says what to do with each path.
|
|
349
|
+
|
|
350
|
+
This avoids ambiguous writeback behavior and gives users a clear rule: if a CLI write should persist, configure that composite profile path as `symlink` and provide or accept the profile/native file that should receive the mutation.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Outfitter documentation
|
|
2
|
+
|
|
3
|
+
User-facing Outfitter documentation.
|
|
4
|
+
|
|
5
|
+
- [Getting started](./getting-started.md)
|
|
6
|
+
- [Concepts](./concepts.md)
|
|
7
|
+
- [CLI reference](./cli.md)
|
|
8
|
+
- [First-time CLI agent users](./first-time-cli-agent-users.md)
|
|
9
|
+
- [Switching to Outfitter](./switching-to-outfitter.md)
|
|
10
|
+
- [Profiles](./profiles.md)
|
|
11
|
+
- [Profile repositories](./profile-repository.md)
|
|
12
|
+
- [Iterating on local and worktree profiles](./iterating-on-profiles.md)
|
|
13
|
+
- [Adapter support matrix](./support-matrix.md)
|
|
14
|
+
- [State persistence](./state.md)
|
|
15
|
+
- [Philosophy](../philosophy.md)
|
|
16
|
+
|
|
17
|
+
## Use cases
|
|
18
|
+
|
|
19
|
+
- [Organization profile catalog](./usecases/organization-profile-catalog.md) — Publish shared team roles so new users can start with organization-approved defaults.
|
|
20
|
+
- [Engineering profile catalog](./usecases/engineering.md) — Package coding, platform, and review profiles for repeatable engineering workflows.
|
|
21
|
+
- [Persona reviews](./usecases/persona-reviews.md) — Create customer personas to get feedback on ideas, documentation, and designs.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# CLI reference
|
|
2
|
+
|
|
3
|
+
Global options:
|
|
4
|
+
|
|
5
|
+
| Option | Description |
|
|
6
|
+
| --------------- | ---------------------------- |
|
|
7
|
+
| `-V, --version` | Print the Outfitter version. |
|
|
8
|
+
| `-h, --help` | Show help for a command. |
|
|
9
|
+
|
|
10
|
+
## `outfitter run [args...]`
|
|
11
|
+
|
|
12
|
+
Assemble a composite profile and launch the selected agent CLI. `run` is the default command, so plain `outfitter` and `outfitter run` are equivalent.
|
|
13
|
+
|
|
14
|
+
| Option | Description |
|
|
15
|
+
| ------------------------- | -------------------------------------------------------------------------- |
|
|
16
|
+
| `-p, --profile <profile>` | Outfitter profile id to run. Defaults to the settings `default_profile`. |
|
|
17
|
+
| `--agent <agent>` | Agent adapter to launch: `pi` or `claude`. Defaults to `default_agent`. |
|
|
18
|
+
| `--strict` | Fail instead of warning when controls cannot be translated by the adapter. |
|
|
19
|
+
|
|
20
|
+
Any other arguments and unrecognized options are passed through to the launched agent CLI:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
outfitter run --profile engineer --agent claude
|
|
24
|
+
outfitter -p data_analyst -- --print "summarize this repo"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
On a first interactive launch with no `~/.outfitter/settings.yml`, `outfitter` starts Pi-native onboarding instead of a normal run.
|
|
28
|
+
|
|
29
|
+
## `outfitter setup [source]`
|
|
30
|
+
|
|
31
|
+
Create initial Outfitter settings and a default profile. Setup launches Pi with the Outfitter onboarding extension and finishes profile selection inside the agent session (see [Getting started](./getting-started.md)).
|
|
32
|
+
|
|
33
|
+
| Argument | Description |
|
|
34
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `[source]` | Optional setup source: a local path or a git URL of a [profile repository](./profile-repository.md) to import. |
|
|
36
|
+
|
|
37
|
+
## `outfitter sync`
|
|
38
|
+
|
|
39
|
+
Synchronize URI-backed profile and remote settings sources into the local cache (`~/.outfitter/cache/`). Takes no options. Reports a per-source status of `updated`, `unchanged`, `skipped`, or `failed`, and validates synced profile sources.
|
|
40
|
+
|
|
41
|
+
## `outfitter profile`
|
|
42
|
+
|
|
43
|
+
List and manage Outfitter profiles.
|
|
44
|
+
|
|
45
|
+
### `outfitter profile list`
|
|
46
|
+
|
|
47
|
+
List available Outfitter profiles.
|
|
48
|
+
|
|
49
|
+
| Option | Description |
|
|
50
|
+
| ------- | ----------------------------------------------------------------- |
|
|
51
|
+
| `--all` | Include template profiles that are intended only for inheritance. |
|
|
52
|
+
|
|
53
|
+
### `outfitter profile create <name>`
|
|
54
|
+
|
|
55
|
+
Create a new Outfitter profile skeleton.
|
|
56
|
+
|
|
57
|
+
| Argument / option | Description |
|
|
58
|
+
| ----------------- | --------------------------------------------------------- |
|
|
59
|
+
| `<name>` | Filesystem-safe profile name. |
|
|
60
|
+
| `--scope <scope>` | Destination scope: `user`, `project`, or `project-local`. |
|
|
61
|
+
| `--path <path>` | Destination profile source directory. |
|
|
62
|
+
|
|
63
|
+
### `outfitter profile lint`
|
|
64
|
+
|
|
65
|
+
Validate profiles, inheritance, and typed prompt includes.
|
|
66
|
+
|
|
67
|
+
| Option | Description |
|
|
68
|
+
| ---------- | ---------------------------------------- |
|
|
69
|
+
| `--strict` | Exit non-zero when warnings are present. |
|
|
70
|
+
| `--json` | Print diagnostics as JSON. |
|
|
71
|
+
|
|
72
|
+
## `outfitter welcome`
|
|
73
|
+
|
|
74
|
+
Run Outfitter welcome onboarding prompts in the terminal. This is a legacy compatibility command: current onboarding runs natively inside Pi (via `outfitter setup` or the first-run `outfitter` launch), and `welcome` remains for environments that need the older terminal prompt flow. Requires an interactive TTY. Takes no options.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
How an `outfitter` launch goes from configuration files to a running agent:
|
|
4
|
+
|
|
5
|
+
```mermaid
|
|
6
|
+
flowchart LR
|
|
7
|
+
A[Settings] --> B[Profile sources]
|
|
8
|
+
B --> C[Profile stack]
|
|
9
|
+
C --> D[Composite profile]
|
|
10
|
+
D --> E[Adapter]
|
|
11
|
+
E --> F[Agent CLI]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Settings tell Outfitter where profiles come from; profile sources supply profile definitions; the definitions for the selected profile form an ordered stack; the merged stack is written out as a composite profile; an adapter translates that composite profile into agent-specific files, flags, and environment variables; and the agent CLI launches with the result.
|
|
15
|
+
|
|
16
|
+
## Profile
|
|
17
|
+
|
|
18
|
+
A profile is a named, reusable YAML definition of how an agent should be outfitted: model and provider, thinking level, system and append prompts, skills, extensions, subagents, DeepWork jobs, CLI arguments, and environment variables. Profiles can inherit from other profiles and can live as a flat `<id>.yml` file or a directory with a `profile.yml` plus bundled resources. See [Profiles](./profiles.md).
|
|
19
|
+
|
|
20
|
+
## Composite profile
|
|
21
|
+
|
|
22
|
+
A composite profile is the temporary runtime configuration directory Outfitter assembles for one profile and one agent CLI run. It contains the generated files the agent needs, is created under the system temp directory, and is owned by Outfitter for the lifetime of the run — durable state is handled separately (see state persistence below).
|
|
23
|
+
|
|
24
|
+
## Catalog / profile source
|
|
25
|
+
|
|
26
|
+
A profile source is any place profiles are loaded from: a local directory (`path:`), a GitHub repository (`github: owner/repo`), or a git URI (`uri:`). A shared repository of profiles is called a catalog (or profile repository). Remote sources are cached under `~/.outfitter/cache/` and updated with `outfitter sync`; they support `ref` pinning and `only`/`except` filters. See [Profile repositories](./profile-repository.md).
|
|
27
|
+
|
|
28
|
+
## Settings scopes
|
|
29
|
+
|
|
30
|
+
Outfitter reads `settings.yml` from three local scopes — user (`~/.outfitter/settings.yml`), project (`<project>/.outfitter/settings.yml`), and project-local (`<project>/.outfitter/local/settings.yml`, for personal, uncommitted overrides) — plus cached remote settings supplied by `remote_settings` entries. Settings declare the default profile and agent, profile sources, and other launch behavior.
|
|
31
|
+
|
|
32
|
+
## Controls
|
|
33
|
+
|
|
34
|
+
Controls are the generic, agent-neutral knobs a profile sets: `model`, `provider`, `thinking`, `system_prompt`, `append_system_prompt`, `skills`, `extensions`, `args`, `environment`, and more. Profiles can also nest adapter-specific overrides under `controls.pi` or `controls.claude` when one agent needs different values.
|
|
35
|
+
|
|
36
|
+
## Adapters
|
|
37
|
+
|
|
38
|
+
An adapter translates generic controls 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 a control it warns to stderr, or fails when `--strict` is set. See the [adapter support matrix](./support-matrix.md) for per-adapter coverage.
|
|
39
|
+
|
|
40
|
+
## State persistence
|
|
41
|
+
|
|
42
|
+
Agents write state during a run — auth, native settings, plugins, sessions. Each adapter declares the state paths it understands and how writes are handled (`symlink`, `discard`, `warn`, `error`, or `prompt`), so useful state survives future runs without Outfitter silently copying unknown files. See [State persistence](./state.md).
|
|
43
|
+
|
|
44
|
+
## Layer precedence
|
|
45
|
+
|
|
46
|
+
When several layers define the same profile or setting, higher layers win:
|
|
47
|
+
|
|
48
|
+
1. Project-local (`.outfitter/local/`)
|
|
49
|
+
2. Project (`.outfitter/`)
|
|
50
|
+
3. User (`~/.outfitter/`)
|
|
51
|
+
4. Cached remote sources (in configured source order)
|
|
52
|
+
5. Built-in defaults
|
|
53
|
+
|
|
54
|
+
For profiles, explicitly inherited profiles slot between cached remote sources and built-in defaults, in declared order.
|