@ai-outfitter/outfitter 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.outfitter/skills/outfitter/SKILL.md +4 -0
  2. package/README.md +5 -1
  3. package/dist/cli/OutfitterCli.js +2 -0
  4. package/dist/cli/OutfitterCli.js.map +1 -1
  5. package/dist/cli/commands/RunAgentCommand.d.ts +5 -0
  6. package/dist/cli/commands/RunAgentCommand.js +16 -1
  7. package/dist/cli/commands/RunAgentCommand.js.map +1 -1
  8. package/dist/cli/commands/SetupCommand.d.ts +2 -1
  9. package/dist/cli/commands/SetupCommand.js +1 -0
  10. package/dist/cli/commands/SetupCommand.js.map +1 -1
  11. package/dist/cli/commands/SourcesCommand.d.ts +19 -0
  12. package/dist/cli/commands/SourcesCommand.js +85 -0
  13. package/dist/cli/commands/SourcesCommand.js.map +1 -0
  14. package/dist/cli/commands/SyncCommand.js +8 -0
  15. package/dist/cli/commands/SyncCommand.js.map +1 -1
  16. package/dist/schemas/settings.schema.json +5 -0
  17. package/dist/settings/Settings.d.ts +6 -0
  18. package/dist/settings/Settings.js +1 -0
  19. package/dist/settings/Settings.js.map +1 -1
  20. package/dist/settings/SettingsLoader.js +1 -0
  21. package/dist/settings/SettingsLoader.js.map +1 -1
  22. package/dist/settings/SettingsMerger.js +3 -0
  23. package/dist/settings/SettingsMerger.js.map +1 -1
  24. package/dist/setup/DefaultCatalog.js +18 -2
  25. package/dist/setup/DefaultCatalog.js.map +1 -1
  26. package/dist/sources/SourceCachePolicy.d.ts +11 -0
  27. package/dist/sources/SourceCachePolicy.js +48 -0
  28. package/dist/sources/SourceCachePolicy.js.map +1 -0
  29. package/dist/sources/SourceState.d.ts +32 -0
  30. package/dist/sources/SourceState.js +90 -0
  31. package/dist/sources/SourceState.js.map +1 -0
  32. package/docs/documentation/README.md +5 -3
  33. package/docs/documentation/ai-outfitter.md +259 -0
  34. package/docs/documentation/assets/feature-to-pr.svg +70 -0
  35. package/docs/documentation/cli.md +8 -0
  36. package/docs/documentation/containers.md +3 -80
  37. package/docs/documentation/settings.md +5 -0
  38. package/package.json +1 -1
  39. package/src/schemas/settings.schema.json +5 -0
@@ -4,6 +4,7 @@ import { join } from 'node:path';
4
4
  import { runGit, syncRemoteRepositoryAtomically, tryReadCleanHead } from '../sources/GitRepository.js';
5
5
  import { createRemoteRepositoryCachePath, encodeRemoteSourceSelection, formatRemoteSourceDisplay, isImmutableRef, isVersionTagRef, redactEmbeddedSourceCredentials, } from '../sources/SourceCache.js';
6
6
  import { readDeclaredRemoteSources } from '../sources/TransitiveSources.js';
7
+ import { writeSourceState } from '../sources/SourceState.js';
7
8
  export const defaultCatalogSource = {
8
9
  github: 'ai-outfitter/default-profiles',
9
10
  // TODO(release-automation): when default-profiles publishes a new Release Please tag, open a
@@ -45,10 +46,18 @@ export const bootstrapPinnedCatalog = (input) => {
45
46
  assertImmutableRef(input.source.ref);
46
47
  const hasPayload = input.requirePayload ?? hasAgentsDirectory;
47
48
  const root = createRemoteRepositoryCachePath(input.homeDirectory, input.source, input.cacheDirectory);
48
- if (isPinnedCatalogCheckout(root, input.source.ref, hasPayload))
49
+ if (isPinnedCatalogCheckout(root, input.source.ref, hasPayload)) {
50
+ writeSourceState({
51
+ homeDirectory: input.homeDirectory,
52
+ cacheDirectory: input.cacheDirectory,
53
+ cachePath: root,
54
+ source: input.source,
55
+ commit: resolveExpectedCommit(root, input.source.ref),
56
+ });
49
57
  return { root, source: input.source };
58
+ }
50
59
  try {
51
- (input.syncRepository ?? syncRemoteRepositoryAtomically)({
60
+ const synced = (input.syncRepository ?? syncRemoteRepositoryAtomically)({
52
61
  cachePath: root,
53
62
  fetchRef: isVersionTagRef(input.source.ref)
54
63
  ? `refs/tags/${input.source.ref}:refs/tags/${input.source.ref}`
@@ -65,6 +74,13 @@ export const bootstrapPinnedCatalog = (input) => {
65
74
  return 0;
66
75
  },
67
76
  });
77
+ writeSourceState({
78
+ homeDirectory: input.homeDirectory,
79
+ cacheDirectory: input.cacheDirectory,
80
+ cachePath: root,
81
+ source: input.source,
82
+ commit: synced.commit,
83
+ });
68
84
  return { root, source: input.source };
69
85
  }
70
86
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultCatalog.js","sourceRoot":"","sources":["../../src/setup/DefaultCatalog.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DefaultCatalog.js","sourceRoot":"","sources":["../../src/setup/DefaultCatalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { SourceCachePolicy } from '../settings/Settings.js';
2
+ export interface PrepareSourceCachesInput {
3
+ readonly homeDirectory: string;
4
+ readonly projectDirectory: string;
5
+ readonly policy: SourceCachePolicy;
6
+ }
7
+ export interface PrepareSourceCachesResult {
8
+ readonly messages: readonly string[];
9
+ }
10
+ /** Establishes every declared remote before resolution; local layers are never passed to sync. */
11
+ export declare const prepareSourceCaches: (input: PrepareSourceCachesInput) => PrepareSourceCachesResult;
@@ -0,0 +1,48 @@
1
+ import { mkdirSync, renameSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { executeSyncCommand } from '../cli/commands/SyncCommand.js';
4
+ import { syncRemoteRepositoryAtomically } from './GitRepository.js';
5
+ import { inspectSourceCache, unchangedResult, withSourceLock, writeSourceState } from './SourceState.js';
6
+ const fullCommitPattern = /^[a-f0-9]{40}$/u;
7
+ const quarantine = (cachePath) => {
8
+ const destination = `${cachePath}.outfitter-quarantine-${new Date().toISOString().replace(/[:.]/gu, '-')}-${process.pid}`;
9
+ mkdirSync(dirname(destination), { recursive: true });
10
+ renameSync(cachePath, destination);
11
+ return destination;
12
+ };
13
+ const policyRepositorySync = (input) => (request) => withSourceLock(request.cachePath, () => {
14
+ if (input.policy === 'locked' && !fullCommitPattern.test(request.source.ref ?? '')) {
15
+ throw new Error('Locked source-cache policy requires a full 40-character commit pin.');
16
+ }
17
+ const state = inspectSourceCache({
18
+ homeDirectory: input.homeDirectory,
19
+ cachePath: request.cachePath,
20
+ source: request.source,
21
+ });
22
+ if (state.health === 'healthy' && state.commit !== undefined)
23
+ return unchangedResult(state.commit);
24
+ if (input.policy === 'offline') {
25
+ throw new Error(`Source cache is ${state.health}; offline policy prohibits repair.`);
26
+ }
27
+ const quarantined = state.health === 'dirty' ? quarantine(request.cachePath) : undefined;
28
+ const result = syncRemoteRepositoryAtomically(request);
29
+ writeSourceState({
30
+ homeDirectory: input.homeDirectory,
31
+ source: request.source,
32
+ commit: result.commit,
33
+ cachePath: request.cachePath,
34
+ });
35
+ return {
36
+ ...result,
37
+ warnings: result.warnings + (quarantined === undefined ? 0 : 1),
38
+ };
39
+ });
40
+ /** Establishes every declared remote before resolution; local layers are never passed to sync. */
41
+ export const prepareSourceCaches = (input) => {
42
+ const result = executeSyncCommand({ homeDirectory: input.homeDirectory, projectDirectory: input.projectDirectory }, { syncRepository: policyRepositorySync(input) });
43
+ if (result.exitCode !== 0) {
44
+ throw new Error(`Source-cache ${input.policy} policy failed before composition: ${result.messages.join('; ')}`);
45
+ }
46
+ return { messages: result.messages };
47
+ };
48
+ //# sourceMappingURL=SourceCachePolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SourceCachePolicy.js","sourceRoot":"","sources":["../../src/sources/SourceCachePolicy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { AtomicRepositorySyncResult } from './GitRepository.js';
2
+ import type { RemoteSourceReference } from './SourceCache.js';
3
+ export declare const SOURCE_STATE_VERSION = 1;
4
+ export interface SourceStateManifest {
5
+ readonly version: 1;
6
+ readonly source: string;
7
+ readonly requestedRef: string | null;
8
+ readonly resolvedCommit: string;
9
+ readonly cacheKey: string;
10
+ }
11
+ export type SourceCacheHealth = 'healthy' | 'missing' | 'dirty' | 'legacy' | 'mismatched' | 'unverified';
12
+ export declare const sourceStatePath: (homeDirectory: string, source: RemoteSourceReference, cacheDirectory?: string) => string;
13
+ export declare const readSourceState: (path: string) => SourceStateManifest | undefined;
14
+ export declare const inspectSourceCache: (input: {
15
+ readonly homeDirectory: string;
16
+ readonly cacheDirectory?: string;
17
+ readonly cachePath: string;
18
+ readonly source: RemoteSourceReference;
19
+ }) => {
20
+ readonly health: SourceCacheHealth;
21
+ readonly commit?: string;
22
+ readonly manifest?: SourceStateManifest;
23
+ };
24
+ export declare const writeSourceState: (input: {
25
+ readonly homeDirectory: string;
26
+ readonly cacheDirectory?: string;
27
+ readonly source: RemoteSourceReference;
28
+ readonly commit: string;
29
+ readonly cachePath?: string;
30
+ }) => void;
31
+ export declare const withSourceLock: <T>(cachePath: string, action: () => T) => T;
32
+ export declare const unchangedResult: (commit: string) => AtomicRepositorySyncResult;
@@ -0,0 +1,90 @@
1
+ import { mkdirSync, openSync, closeSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { tryReadCleanHead } from './GitRepository.js';
4
+ import { encodeRemoteSource, normalizeRemoteSourceUri, redactSourceUriCredentials, resolveRemoteRepositoryCacheRoot, } from './SourceCache.js';
5
+ export const SOURCE_STATE_VERSION = 1;
6
+ export const sourceStatePath = (homeDirectory, source, cacheDirectory) => join(resolveRemoteRepositoryCacheRoot(homeDirectory, cacheDirectory), 'source-state', `${encodeRemoteSource(source)}.json`);
7
+ const sourceStatePathForCache = (cachePath, source) => join(dirname(dirname(cachePath)), 'source-state', `${encodeRemoteSource(source)}.json`);
8
+ const sourceIdentity = (source) => redactSourceUriCredentials(normalizeRemoteSourceUri(source));
9
+ export const readSourceState = (path) => {
10
+ try {
11
+ const value = JSON.parse(readFileSync(path, 'utf8'));
12
+ if (value.version !== SOURCE_STATE_VERSION ||
13
+ typeof value.source !== 'string' ||
14
+ !(typeof value.requestedRef === 'string' || value.requestedRef === null) ||
15
+ typeof value.resolvedCommit !== 'string' ||
16
+ typeof value.cacheKey !== 'string')
17
+ return undefined;
18
+ return value;
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ };
24
+ export const inspectSourceCache = (input) => {
25
+ const commit = tryReadCleanHead(input.cachePath);
26
+ if (commit === undefined) {
27
+ try {
28
+ readFileSync(join(input.cachePath, '.git', 'HEAD'));
29
+ return { health: 'dirty' };
30
+ }
31
+ catch {
32
+ return { health: 'missing' };
33
+ }
34
+ }
35
+ const manifest = readSourceState(sourceStatePathForCache(input.cachePath, input.source));
36
+ if (manifest === undefined)
37
+ return { health: 'legacy', commit };
38
+ if (manifest.source !== sourceIdentity(input.source) ||
39
+ manifest.requestedRef !== (input.source.ref ?? null) ||
40
+ manifest.cacheKey !== encodeRemoteSource(input.source) ||
41
+ manifest.resolvedCommit !== commit)
42
+ return { health: 'mismatched', commit, manifest };
43
+ return { health: 'healthy', commit, manifest };
44
+ };
45
+ export const writeSourceState = (input) => {
46
+ const path = input.cachePath === undefined
47
+ ? sourceStatePath(input.homeDirectory, input.source, input.cacheDirectory)
48
+ : sourceStatePathForCache(input.cachePath, input.source);
49
+ mkdirSync(dirname(path), { recursive: true });
50
+ const temporary = `${path}.${process.pid}.tmp`;
51
+ const manifest = {
52
+ version: SOURCE_STATE_VERSION,
53
+ source: sourceIdentity(input.source),
54
+ requestedRef: input.source.ref ?? null,
55
+ resolvedCommit: input.commit,
56
+ cacheKey: encodeRemoteSource(input.source),
57
+ };
58
+ writeFileSync(temporary, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 });
59
+ renameSync(temporary, path);
60
+ };
61
+ export const withSourceLock = (cachePath, action) => {
62
+ const lock = `${cachePath}.outfitter.lock`;
63
+ mkdirSync(dirname(lock), { recursive: true });
64
+ const deadline = Date.now() + 300_000;
65
+ let descriptor;
66
+ while (descriptor === undefined) {
67
+ try {
68
+ descriptor = openSync(lock, 'wx', 0o600);
69
+ }
70
+ catch (error) {
71
+ /* v8 ignore next -- non-EEXIST open failures and a five-minute lock timeout are OS boundaries. */
72
+ if (error.code !== 'EEXIST' || Date.now() >= deadline)
73
+ throw error;
74
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
75
+ }
76
+ }
77
+ try {
78
+ return action();
79
+ }
80
+ finally {
81
+ closeSync(descriptor);
82
+ rmSync(lock, { force: true });
83
+ }
84
+ };
85
+ export const unchangedResult = (commit) => ({
86
+ commit,
87
+ status: 'unchanged',
88
+ warnings: 0,
89
+ });
90
+ //# sourceMappingURL=SourceState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SourceState.js","sourceRoot":"","sources":["../../src/sources/SourceState.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,10 @@
1
1
  # Outfitter documentation
2
2
 
3
- Outfitter lays out conventions for iterating on and sharing agent configuration — agent profiles, skills, and loadouts. The docs follow that arc: **set up** your own, **understand** the pieces, **share** them across projects and your org, **automate** them on more surfaces, then contribute back. Each page keeps to one concept and links onward when you need the next one, so nothing here has to be read up front.
3
+ Outfitter lays out conventions for iterating on and sharing agent configuration — agent profiles, skills, and loadouts. Start by setting up your configuration, then learn how to share it across projects and your organization and run it in CI or on a cluster. Each page keeps to one concept and links onward when you need the next one, so nothing here has to be read up front.
4
4
 
5
- Onboarding an organization? Start with
5
+ To understand AI Outfitter and how its projects fit together, read
6
+ [How AI Outfitter fits together](./ai-outfitter.md). To assess and onboard your
7
+ own organization, start with
6
8
  [Onboard an organization with an SDLC report](./usecases/org-onboarding-sdlc-report.md) —
7
9
  one engineer runs the maturity assessment, then creates the org `.agents`
8
10
  repository with the baseline report as its first commit.
@@ -48,7 +50,7 @@ The same composition runs on every surface; only the trigger changes.
48
50
 
49
51
  - [Running an agent in GitHub Actions](./actions.md) — headless runs on any workflow trigger.
50
52
  - [Channels](./channels.md) — add external event sources that wake a resident agent only when work arrives.
51
- - [Container images](./containers.md) — run the published Debian-based image persistently, extend it with apt, or use the `-nix` variant.
53
+ - [Container images](./containers.md) — run the published Debian-based image persistently or extend it with apt.
52
54
  - [Recurring runs](./recurring-runs.md) — loops three ways: the local loop extension, Actions cron, cluster schedules.
53
55
  - [Cost estimation](./cost-estimation.md) — one workload profile and model-cost line across local, Actions, and Kubernetes runs.
54
56
  - [In-cluster agents](./in-cluster.md) — resident agents, CronJobs, and subagent Jobs via Link Operator.
@@ -0,0 +1,259 @@
1
+ # How AI Outfitter fits together
2
+
3
+ Open, vendor-neutral tooling for configuring and operating agents: ramp a
4
+ user, a team, or an organization from AI-assisted coding to an autonomous
5
+ software development lifecycle.
6
+
7
+ Everything here builds on one convention: an agent's context, tools, skills,
8
+ and permissions are plain files in a `.agents/` directory — committed,
9
+ reviewed, and shared like the rest of your code. The same plain-file definition
10
+ can be shared across environments and model vendors; runtime support varies by
11
+ harness, as shown in the
12
+ [support matrix](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/support-matrix.md).
13
+ The core is open source under MIT.
14
+
15
+ ## Why we built AI Outfitter
16
+
17
+ We built AI Outfitter to give teams a shared path from local agent use to
18
+ automated and governed workflows. Its defaults are customizable, so teams can
19
+ begin at the rung that matches their current process.
20
+
21
+ ## The adoption ramp
22
+
23
+ Five rungs, from AI-assisted coding to an autonomous lifecycle
24
+ ([full definition](https://github.com/ai-outfitter/outfitter/blob/main/docs/philosophy.md)).
25
+ Each Outfitter component targets a rung, so you climb without rebuilding what
26
+ got you here — and without adopting complexity too early. Rungs 2, 3, and 4
27
+ each open with the **runbook** that gets you there: the concrete steps, and a
28
+ success check you run rather than judge.
29
+
30
+ 1. **Assisted** — autocomplete and chat; a human's hands stay on the
31
+ keyboard. _You are here if_ you use autocomplete in an IDE.
32
+ There is nothing to govern yet — but the habit that matters starts here:
33
+ document what works and what doesn't in `AGENTS.md`/`CLAUDE.md`, and
34
+ keep it in the repo.
35
+ - [First-time CLI agent users](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/first-time-cli-agent-users.md#context-engineering)
36
+ — what belongs in a first `AGENTS.md`, and how to ask an agent to use
37
+ it.
38
+
39
+ 2. **Delegated** — a local agent does the task; you define the idea and
40
+ review the PR. _You are here if_ engineers run a coding agent in a
41
+ terminal and push the result. This is where configuration starts to
42
+ matter.
43
+ - **Runbook: [Share one catalog](https://github.com/ai-outfitter/outfitter/blob/main/docs/runbooks/share-one-catalog.md)**
44
+ — one pinned catalog the organization shares, instead of per-laptop
45
+ configuration.
46
+ - [outfitter](https://github.com/ai-outfitter/outfitter) — composes what
47
+ an agent knows and may do into a **profile**: plain files in your
48
+ `.agents/` folder, reviewed like code and portable across environments
49
+ and **harnesses** (the CLI that runs an agent: Claude Code, Pi, Codex).
50
+ - [deepwork](https://github.com/ai-outfitter/deepwork) — step-by-step
51
+ quality gates so the agent checks its own work.
52
+
53
+ 3. **Automated** — a workflow runs without your laptop: an issue, a message,
54
+ or a schedule triggers agents in CI, on a cluster, or on a remote server
55
+ you never sit at; adversarial review is part of the pipeline; session
56
+ logs are captured before merge. _You are here when_ you close your laptop
57
+ and the work keeps going. What promotes you is the trigger, not the
58
+ hardware: an agent you drive over SSH is rung 2 on a bigger machine.
59
+ - **Runbook: [Run it without your laptop](https://github.com/ai-outfitter/outfitter/blob/main/docs/runbooks/run-without-your-laptop.md)**
60
+ — an event triggers the workflow, its output lands through review, and
61
+ the session is captured.
62
+ - [actions](https://github.com/ai-outfitter/actions) — runs any profile
63
+ headless in GitHub Actions, on any trigger.
64
+ - [channels](https://github.com/ai-outfitter/channels) — pushes email,
65
+ Slack, Signal, and forge events (GitHub or GitLab activity) into an
66
+ agent session, so one message can start the same workflow.
67
+ - [agent-operator](https://github.com/ai-outfitter/agent-operator) —
68
+ hosts the same profiles on your own infrastructure, well before you
69
+ need its resident-agent story on the next rung.
70
+
71
+ 4. **Governed** — the organization shares one version-pinned catalog of
72
+ agents, skills, and policy; every agent action lands in an auditable
73
+ record; **[resident agents](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/in-cluster.md)**
74
+ — long-lived agents onboarded like teammates,
75
+ with their own accounts and boundaries — take on standing jobs. _You are
76
+ here when_ agents work across many teams, and the organization needs
77
+ shared policy — and proof of what every agent did.
78
+ - **Runbook: [Give the agent a residence](https://github.com/ai-outfitter/outfitter/blob/main/docs/runbooks/give-the-agent-a-residence.md)**
79
+ — a named, assignable agent with an account and somewhere to live.
80
+ - **Build your agent catalog: [Share one catalog](https://github.com/ai-outfitter/outfitter/blob/main/docs/runbooks/share-one-catalog.md)**
81
+ — the rung 2 runbook creates it and links to guidance on its layout,
82
+ version pinning, and governance. Our own
83
+ [.agents](https://github.com/ai-outfitter/.agents) is a worked example
84
+ you can read end to end.
85
+ - [agent-operator](https://github.com/ai-outfitter/agent-operator) —
86
+ provisions and supervises resident agents on your own infrastructure.
87
+ - [pensieve](https://github.com/ai-outfitter/pensieve) — designed to retain
88
+ evidence from agent sessions, including environments, agents, tools,
89
+ artifacts, and costs. That record can support audits, compliance reviews,
90
+ and evaluation workflows.
91
+
92
+ 5. **Self-improving** — the audit record feeds evals and improvement; humans
93
+ set goals and acceptance gates, agents own the middle. Rung 5 is where
94
+ the stack is thinnest today.
95
+ - [evals](https://github.com/ai-outfitter/evals) — evaluates changes to a
96
+ profile, model, or workflow with reproducible, attested benchmarks.
97
+ - [autoimprove](https://github.com/ai-outfitter/autoimprove) — improves
98
+ portable skills against measured outcomes.
99
+
100
+ ## Start with one workflow end to end
101
+
102
+ One useful first workflow takes **a feature idea to a merged PR**, automated
103
+ end to end, with every step leaving evidence. This is what rung 3 looks like
104
+ up close.
105
+
106
+ ![A feature idea flows through plan, implement, and adversarial review to a merged PR, with every transition writing to the evidence record](./assets/feature-to-pr.svg)
107
+
108
+ 1. **Entry point.** Someone files an issue and assigns it to an agent — from
109
+ the forge, from chat, or from a planning session at a desk. Different
110
+ doors, same workflow.
111
+ 2. **Plan.** A planner profile with read-only tools turns the issue into a
112
+ spec artifact and posts it back to the issue, where a human can approve
113
+ it (we recommend an enforced spec system, like
114
+ [2119](https://github.com/Unsupervisedcom/2119), for more autonomous
115
+ workflows).
116
+ 3. **Implement.** An implementer profile picks up the approved spec in a
117
+ fresh container with exactly the tools the work needs — a different
118
+ agent, a clean context window, the same shared catalog.
119
+ 4. **Review.** An adversarial reviewer profile, which shares none of the
120
+ implementer's context, tries to break the change before any human reads
121
+ it.
122
+ 5. **Merge.** The PR arrives with its history attached: session transcripts,
123
+ tool calls, and diffs captured as artifacts before the environment that
124
+ produced them is torn down.
125
+
126
+ The same shape handles other starting workflows. A vulnerability report
127
+ instead of a feature idea turns the pipeline into governed security
128
+ remediation: the scanner files the issue (most scanners already can), the
129
+ planner scopes the fix, and the same steps carry it to a tested, approved
130
+ PR. Bug reports run the same way with a triage step in front: an agent
131
+ reproduces and prioritizes each report, and only the ones that clear triage
132
+ enter the pipeline.
133
+
134
+ Teams adopt the system through the same composition process: an engineer
135
+ refines a skill in their own
136
+ [`~/.agents`](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/local-development.md)
137
+ against real work; the team mines
138
+ [pensieve](https://github.com/ai-outfitter/pensieve) for the patterns behind
139
+ successful and failing runs. When a change earns trust it moves by pull
140
+ request into the org catalog, where every agent composes it by name. One
141
+ person's improvement becomes everyone's default at the next pin bump — no
142
+ one else reconfigures anything.
143
+
144
+ ## Start this afternoon
145
+
146
+ You can begin with a local assessment and CLI trial before committing to an
147
+ organization-wide rollout or additional infrastructure.
148
+
149
+ ### 1. See where you are
150
+
151
+ The read-only assessment takes one afternoon. The
152
+ [org-onboarding runbook](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/usecases/org-onboarding-sdlc-report.md)
153
+ produces a baseline **SDLC report**: where your organization sits on the
154
+ ramp, with evidence, gaps, and next-rung recommendations. Concretely: one
155
+ engineer runs the `sdlc-report` skill from their own local agent with a
156
+ read-only forge token. It reads through the API — never clones, never
157
+ writes — and produces two local files you review before anyone else sees
158
+ them. We ran it on this organization the day we wrote this page; the output
159
+ is committed at
160
+ [.agents/reports/sdlc](https://github.com/ai-outfitter/.agents/tree/main/reports/sdlc),
161
+ so you can see exactly what you would get.
162
+
163
+ ### 2. Try the toolchain
164
+
165
+ The first run takes about ten minutes.
166
+
167
+ ```bash
168
+ npx @ai-outfitter/outfitter
169
+ ```
170
+
171
+ This launches the Outfitter CLI with the
172
+ [Pi](https://github.com/earendil-works/pi) harness bundled and walks you
173
+ through composing your first agent profile from a starter catalog. You end
174
+ in a working agent session, and everything it created is plain files under
175
+ `~/.agents` — which you can read, edit, or delete afterward.
176
+
177
+ ### 3. Automate one workflow
178
+
179
+ Pick feature-to-PR or bug-to-PR, keep it to
180
+ one repository, and promote the profiles you already trust at a desk into
181
+ [CI](https://github.com/ai-outfitter/actions). Work the runbooks in order —
182
+ [share one catalog](https://github.com/ai-outfitter/outfitter/blob/main/docs/runbooks/share-one-catalog.md),
183
+ [run it without your laptop](https://github.com/ai-outfitter/outfitter/blob/main/docs/runbooks/run-without-your-laptop.md),
184
+ then [give the agent a residence](https://github.com/ai-outfitter/outfitter/blob/main/docs/runbooks/give-the-agent-a-residence.md).
185
+ Each ends with the one concrete step that starts the next.
186
+
187
+ Every runbook closes with a **Done when** section naming the signals the report
188
+ above checks — for this one, `triggered-agents`, `protected-landing`, and
189
+ `session-capture`. Re-run the report and it names whichever is still unmet.
190
+
191
+ ## Agent configuration as code
192
+
193
+ Your agent setup is already configuration: system prompts, skills, MCP
194
+ servers, model choices, permissions. Today that configuration lives per tool
195
+ and per laptop, gets pasted between repositories, and drifts. Every other
196
+ kind of configuration your organization depends on graduated from that stage
197
+ years ago — into files, in a repository, behind review.
198
+
199
+ The [`.agents` convention](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/concepts.md#the-agents-protocol)
200
+ is an open standard for doing the same for agents:
201
+
202
+ ```text
203
+ .agents/
204
+ agents.md # shared operating context
205
+ system-prompt.md # base system prompt
206
+ mcp.json # MCP servers
207
+ models.json # model configuration
208
+ agents/<id>/agent.md # agent identities + loadouts
209
+ skills/<id>/... # capability packages
210
+ knowledge/ # reference documents
211
+ commands/ # slash commands
212
+ ```
213
+
214
+ Because the configuration is Markdown and JSON, you can read, review, and
215
+ audit it with ordinary repository tools. Layers merge by name — a project's
216
+ `.agents/` over an engineer's `~/.agents/` over the organization's
217
+ **[catalog](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/catalogs.md)**,
218
+ a shared collection pinned by version — so individuals keep their preferences and
219
+ organizations keep their policy
220
+ ([conventions](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/conventions.md)).
221
+
222
+ Composition beats accumulation. A profile is a selection from those files,
223
+ and profiles stack: a personal baseline, a team convention, a project role,
224
+ switched as the work changes. That keeps each profile tight, and tight
225
+ profiles preserve the context headroom that turns into faster, better
226
+ sessions.
227
+
228
+ The directory is also the exit door. It is the source of truth, and it is
229
+ useful without Outfitter. Vendor-neutral cuts in every direction — models,
230
+ harnesses, and us. Swap model vendors freely. Run the catalog through any
231
+ harness: Pi has the deepest runtime support today, with Claude Code tracked
232
+ component by component in the
233
+ [support matrix](https://github.com/ai-outfitter/outfitter/blob/main/docs/documentation/support-matrix.md)
234
+ — a Claude Code team starts on Claude Code, and the matrix shows the gaps
235
+ before you hit them. And if you drop Outfitter itself, the catalog you
236
+ built is still yours — plain files, still working.
237
+
238
+ ## Where this is today
239
+
240
+ Outfitter and several supporting projects are in active development. Actions
241
+ and the catalogs run AI Outfitter's own workloads today; rung 5 remains the
242
+ least developed part of the stack. Check each linked repository for its current
243
+ implementation status.
244
+
245
+ The model is open core: the convention, the toolchain, and the defaults are
246
+ MIT — modules you can rip out and replace — while some advanced capabilities
247
+ ship under an enterprise license. If you are evaluating this for an
248
+ organization, [open an issue](https://github.com/ai-outfitter/outfitter/issues)
249
+ with what you found. The gaps you hit are the roadmap we want.
250
+
251
+ ## As you climb
252
+
253
+ Two suggestions we make strongly, and follow ourselves:
254
+
255
+ 1. **Automate nothing you have not first done manually.**
256
+ 2. **Hand over control one layer at a time.**
257
+
258
+ You never skip a step you don't understand, and nothing you build on one
259
+ rung is thrown away on the next.
@@ -0,0 +1,70 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 284" font-family="-apple-system, 'Segoe UI', Helvetica, Arial, sans-serif">
2
+ <defs>
3
+ <marker id="arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
4
+ <path d="M 0 1 L 8 5 L 0 9 z" fill="#57606a"/>
5
+ </marker>
6
+ <marker id="arrdim" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
7
+ <path d="M 0 1 L 8 5 L 0 9 z" fill="#d4a72c"/>
8
+ </marker>
9
+ </defs>
10
+
11
+ <rect x="0.5" y="0.5" width="979" height="283" rx="12" fill="#ffffff" stroke="#d0d7de"/>
12
+
13
+ <!-- step boxes -->
14
+ <g>
15
+ <rect x="28" y="36" width="168" height="88" rx="8" fill="#f6f8fa" stroke="#d0d7de"/>
16
+ <text x="112" y="58" text-anchor="middle" font-size="10.5" fill="#0969da" font-weight="600" letter-spacing="0.5">1 · ENTRY POINT</text>
17
+ <text x="112" y="78" text-anchor="middle" font-size="14" fill="#1f2328" font-weight="600">Issue assigned</text>
18
+ <text x="112" y="96" text-anchor="middle" font-size="11" fill="#57606a">from the forge, chat,</text>
19
+ <text x="112" y="110" text-anchor="middle" font-size="11" fill="#57606a">or a desk session</text>
20
+ </g>
21
+ <g>
22
+ <rect x="217" y="36" width="168" height="88" rx="8" fill="#f6f8fa" stroke="#d0d7de"/>
23
+ <text x="301" y="58" text-anchor="middle" font-size="10.5" fill="#0969da" font-weight="600" letter-spacing="0.5">2 · PLAN</text>
24
+ <text x="301" y="78" text-anchor="middle" font-size="14" fill="#1f2328" font-weight="600">Planner agent</text>
25
+ <text x="301" y="96" text-anchor="middle" font-size="11" fill="#57606a">read-only tools →</text>
26
+ <text x="301" y="110" text-anchor="middle" font-size="11" fill="#57606a">spec posted for approval</text>
27
+ </g>
28
+ <g>
29
+ <rect x="406" y="36" width="168" height="88" rx="8" fill="#f6f8fa" stroke="#d0d7de"/>
30
+ <text x="490" y="58" text-anchor="middle" font-size="10.5" fill="#0969da" font-weight="600" letter-spacing="0.5">3 · IMPLEMENT</text>
31
+ <text x="490" y="78" text-anchor="middle" font-size="14" fill="#1f2328" font-weight="600">Implementer agent</text>
32
+ <text x="490" y="96" text-anchor="middle" font-size="11" fill="#57606a">fresh container,</text>
33
+ <text x="490" y="110" text-anchor="middle" font-size="11" fill="#57606a">scoped tools, clean context</text>
34
+ </g>
35
+ <g>
36
+ <rect x="595" y="36" width="168" height="88" rx="8" fill="#f6f8fa" stroke="#d0d7de"/>
37
+ <text x="679" y="58" text-anchor="middle" font-size="10.5" fill="#0969da" font-weight="600" letter-spacing="0.5">4 · REVIEW</text>
38
+ <text x="679" y="78" text-anchor="middle" font-size="14" fill="#1f2328" font-weight="600">Adversarial reviewer</text>
39
+ <text x="679" y="96" text-anchor="middle" font-size="11" fill="#57606a">shares no context,</text>
40
+ <text x="679" y="110" text-anchor="middle" font-size="11" fill="#57606a">tries to break the change</text>
41
+ </g>
42
+ <g>
43
+ <rect x="784" y="36" width="168" height="88" rx="8" fill="#f6f8fa" stroke="#d0d7de"/>
44
+ <text x="868" y="58" text-anchor="middle" font-size="10.5" fill="#0969da" font-weight="600" letter-spacing="0.5">5 · MERGE</text>
45
+ <text x="868" y="78" text-anchor="middle" font-size="14" fill="#1f2328" font-weight="600">PR, with history</text>
46
+ <text x="868" y="96" text-anchor="middle" font-size="11" fill="#57606a">transcripts &amp; diffs attached</text>
47
+ <text x="868" y="110" text-anchor="middle" font-size="11" fill="#57606a">before teardown</text>
48
+ </g>
49
+
50
+ <!-- horizontal arrows -->
51
+ <line x1="198" y1="80" x2="213" y2="80" stroke="#57606a" stroke-width="1.5" marker-end="url(#arr)"/>
52
+ <line x1="387" y1="80" x2="402" y2="80" stroke="#57606a" stroke-width="1.5" marker-end="url(#arr)"/>
53
+ <line x1="576" y1="80" x2="591" y2="80" stroke="#57606a" stroke-width="1.5" marker-end="url(#arr)"/>
54
+ <line x1="765" y1="80" x2="780" y2="80" stroke="#57606a" stroke-width="1.5" marker-end="url(#arr)"/>
55
+
56
+ <!-- drop lines to the record -->
57
+ <line x1="112" y1="124" x2="112" y2="162" stroke="#d4a72c" stroke-width="1.2" stroke-dasharray="3 3" marker-end="url(#arrdim)"/>
58
+ <line x1="301" y1="124" x2="301" y2="162" stroke="#d4a72c" stroke-width="1.2" stroke-dasharray="3 3" marker-end="url(#arrdim)"/>
59
+ <line x1="490" y1="124" x2="490" y2="162" stroke="#d4a72c" stroke-width="1.2" stroke-dasharray="3 3" marker-end="url(#arrdim)"/>
60
+ <line x1="679" y1="124" x2="679" y2="162" stroke="#d4a72c" stroke-width="1.2" stroke-dasharray="3 3" marker-end="url(#arrdim)"/>
61
+ <line x1="868" y1="124" x2="868" y2="162" stroke="#d4a72c" stroke-width="1.2" stroke-dasharray="3 3" marker-end="url(#arrdim)"/>
62
+
63
+ <!-- evidence record band -->
64
+ <rect x="28" y="166" width="924" height="52" rx="8" fill="#fff8c5" stroke="#d4a72c" stroke-dasharray="5 4"/>
65
+ <text x="490" y="188" text-anchor="middle" font-size="11" fill="#7d4e00" font-weight="600" letter-spacing="1">THE RECORD</text>
66
+ <text x="490" y="206" text-anchor="middle" font-size="12" fill="#7d4e00">session transcripts · tool calls · diffs · approvals — written at every transition</text>
67
+
68
+ <!-- footer caption -->
69
+ <text x="490" y="252" text-anchor="middle" font-size="12" fill="#57606a" font-style="italic">Each step is an agent profile from a shared catalog — designed to run the same at a desk, in CI, or in a cluster.</text>
70
+ </svg>
@@ -94,3 +94,11 @@ Write the composed resource tree as a self-contained `.agents/` directory for re
94
94
  | `--out <dir>` | Destination directory (default `./.agents`). |
95
95
 
96
96
  > **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).
97
+
98
+ `outfitter run` verifies these caches before composition. Use
99
+ `--source-cache-policy <repair|locked|offline>` to override the configured startup policy.
100
+
101
+ ## `outfitter sources`
102
+
103
+ Report local and remote source precedence, requested and resolved revisions, origins, and cache
104
+ health. `outfitter sources --json` emits stable credential-redacted machine-readable output.