Merge PR #5687: feat(config): add PI_CONFIG_FILES settings overlay env var (@roboomp)
This commit is contained in:
@@ -351,12 +351,13 @@ Extra conditional behavior:
|
||||
|
||||
## 6) Storage and config root paths
|
||||
|
||||
These are consumed via `@oh-my-pi/pi-utils/dirs` and affect where coding-agent stores data.
|
||||
These affect where coding-agent stores data and which process-local settings overlays it loads.
|
||||
|
||||
| Variable | Default / behavior |
|
||||
| --------------------- | ----------------------------------------------------------------------------- |
|
||||
| `PI_CONFIG_DIR` | Config root dirname under home (default `.omp`) |
|
||||
| `PI_CODING_AGENT_DIR` | Full override for agent directory (default `~/<PI_CONFIG_DIR or .omp>/agent`) |
|
||||
| `PI_CONFIG_FILES` | Platform path-list of settings overlays (`:` on Unix, `;` on Windows); loaded in order before explicit `--config` overlays |
|
||||
| `PWD` | Used when matching canonical current working directory in path helpers |
|
||||
|
||||
---
|
||||
|
||||
@@ -122,6 +122,7 @@ Environment variables are **not** a single settings layer. Each is read by the f
|
||||
| `OMP_AUTH_BROKER_URL` | `auth.broker.url` | Env value takes precedence over config. |
|
||||
| `OMP_AUTH_BROKER_TOKEN` | `auth.broker.token` | Env value takes precedence over config. |
|
||||
| `PI_CODING_AGENT_DIR` | (relocates agent dir) | Moves `config.yml`, `agent.db`, and the whole agent base. |
|
||||
| `PI_CONFIG_FILES` | CLI config overlays | Platform path-list (`:` on Unix, `;` on Windows); files load in order before `--config` overlays. |
|
||||
|
||||
Provider API keys are resolved separately (stored auth, OAuth, `models.yml`, environment, and `.env` files); see [Providers](./providers.md) and the full [Environment variables](./environment-variables.md) reference.
|
||||
|
||||
@@ -217,6 +218,10 @@ omp --config ./local/ci-settings.yml "check this failure"
|
||||
omp --config ./base.yml --config ./experiment.yml "try this model"
|
||||
```
|
||||
|
||||
`--config` is accepted by the default launch command, `acp`, and `models`.
|
||||
|
||||
Wrappers may instead set `PI_CONFIG_FILES` to a platform-delimited path list (`:` on Unix, `;` on Windows). Environment overlays load in listed order before explicit `--config` overlays.
|
||||
|
||||
Overlay paths are resolved relative to the process working directory (and `~` is expanded). Each overlay must parse as a YAML mapping; a missing file, invalid YAML, or a top-level array/scalar is a hard error — it does **not** silently fall back to lower-precedence settings.
|
||||
|
||||
## Path-scoped arrays
|
||||
|
||||
@@ -123,6 +123,9 @@
|
||||
|
||||
- Isolated the CLIProxyAPI auth-broker import tests from ambient broker configuration so fixture credentials cannot be uploaded to a live broker ([#5782](https://github.com/can1357/oh-my-pi/issues/5782)).
|
||||
- Fixed disabling **Show Inline Images** leaving previously rendered Kitty graphics over the Ghostty/tmux transcript. The runtime toggle now updates tool and assistant image owners, deletes tracked terminal graphics before replay, and retains hidden read images so they can return when re-enabled.
|
||||
### Added
|
||||
|
||||
- Added `PI_CONFIG_FILES`, a platform-delimited (`:` on Unix, `;` on Windows) environment path-list of settings overlays loaded before `--config` overlays, so wrapper scripts can inject settings without argv surgery ([#5685](https://github.com/can1357/oh-my-pi/issues/5685)).
|
||||
|
||||
## [17.0.1] - 2026-07-16
|
||||
|
||||
|
||||
@@ -355,7 +355,9 @@ export class Settings {
|
||||
this.#cwd = path.normalize(options.cwd ?? getProjectDir());
|
||||
this.#agentDir = path.normalize(options.agentDir ?? getAgentDir());
|
||||
this.#configPath = options.inMemory ? null : path.join(this.#agentDir, MAIN_CONFIG_FILENAMES[0]);
|
||||
this.#configFiles = options.configFiles?.map(file => path.resolve(this.#cwd, expandTilde(file))) ?? [];
|
||||
const configFiles = process.env.PI_CONFIG_FILES?.split(path.delimiter).filter(Boolean) ?? [];
|
||||
if (options.configFiles) configFiles.push(...options.configFiles);
|
||||
this.#configFiles = configFiles.map(file => path.resolve(this.#cwd, expandTilde(file)));
|
||||
this.#persist = !options.inMemory && options.readOnly !== true;
|
||||
|
||||
if (options.overrides) {
|
||||
|
||||
@@ -10,6 +10,24 @@ const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
const fallbackAgentDir = path.join(getConfigRootDir(), "agent");
|
||||
const cliEntry = path.join(import.meta.dir, "..", "src", "cli.ts");
|
||||
|
||||
interface CliProcessResult {
|
||||
exitCode: number;
|
||||
output: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
async function runCliProcess(args: string[], env: NodeJS.ProcessEnv): Promise<CliProcessResult> {
|
||||
const proc = Bun.spawn([process.execPath, cliEntry, ...args], {
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: { ...process.env, NO_COLOR: "1", ...env },
|
||||
});
|
||||
const stdout = new Response(proc.stdout).text();
|
||||
const stderr = new Response(proc.stderr).text();
|
||||
const [exitCode, output, error] = await Promise.all([proc.exited, stdout, stderr]);
|
||||
return { exitCode, output, error };
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetSettingsForTest();
|
||||
testAgentDir = TempDir.createSync("@omp-config-cli-");
|
||||
@@ -169,18 +187,9 @@ describe("config CLI schema coverage", () => {
|
||||
});
|
||||
it("fully flushes JSON larger than a pipe buffer", async () => {
|
||||
if (!testAgentDir) throw new Error("Test agent directory was not initialized");
|
||||
const proc = Bun.spawn([process.execPath, cliEntry, "config", "list", "--json"], {
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...process.env,
|
||||
NO_COLOR: "1",
|
||||
PI_CODING_AGENT_DIR: testAgentDir.path(),
|
||||
},
|
||||
const { exitCode, output, error } = await runCliProcess(["config", "list", "--json"], {
|
||||
PI_CODING_AGENT_DIR: testAgentDir.path(),
|
||||
});
|
||||
const stdout = new Response(proc.stdout).text();
|
||||
const stderr = new Response(proc.stderr).text();
|
||||
const [exitCode, output, error] = await Promise.all([proc.exited, stdout, stderr]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(error).toBe("");
|
||||
@@ -188,4 +197,25 @@ describe("config CLI schema coverage", () => {
|
||||
const parsed: unknown = JSON.parse(output);
|
||||
expect(parsed).toMatchObject({ modelRoles: { type: "record" } });
|
||||
});
|
||||
it("loads PI_CONFIG_FILES overlays in path-list order", async () => {
|
||||
if (!testAgentDir) throw new Error("Test agent directory was not initialized");
|
||||
const baseOverlayPath = path.join(testAgentDir.path(), "base-overlay.yml");
|
||||
const finalOverlayPath = path.join(testAgentDir.path(), "final-overlay.yml");
|
||||
await Promise.all([
|
||||
Bun.write(baseOverlayPath, "defaultThinkingLevel: high\n"),
|
||||
Bun.write(finalOverlayPath, "defaultThinkingLevel: max\n"),
|
||||
]);
|
||||
const { exitCode, output, error } = await runCliProcess(["config", "get", "defaultThinkingLevel", "--json"], {
|
||||
PI_CODING_AGENT_DIR: testAgentDir.path(),
|
||||
PI_CONFIG_FILES: [baseOverlayPath, finalOverlayPath].join(path.delimiter),
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
expect(error).toBe("");
|
||||
expect(JSON.parse(output)).toMatchObject({
|
||||
key: "defaultThinkingLevel",
|
||||
value: "max",
|
||||
type: "enum",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user