Skip to content

Commit e6e204e

Browse files
feloyclaude
andauthored
feat(cli): add --with-agent-settings flag (#103)
* feat(cli): add --with-agent-settings flag Agent settings (user settings files, auto-onboarding, inference config, model selection) are now only generated and staged into the image when --with-agent-settings is explicitly passed. Without the flag, no settings files are computed or copied, making it an opt-in behaviour. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Philippe Martin <phmartin@redhat.com> * fix(test): assert onboarding flag absent instead of file absent The Claude installer always creates .claude.json during installation, so asserting the file is absent without --with-agent-settings was wrong. Assert instead that hasTrustDialogAccepted — written only by skip_onboarding() — is absent when the flag is not passed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Philippe Martin <phmartin@redhat.com> --------- Signed-off-by: Philippe Martin <phmartin@redhat.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 974dc5e commit e6e204e

3 files changed

Lines changed: 106 additions & 15 deletions

File tree

README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The tool assembles the image in layers — base image, agent installation, agent
88

99
1. **Base image** — Ubuntu, Fedora, Red Hat UBI, or Red Hat Hardened Images (HummingBird), any tag. Ubuntu 24.04 is the default.
1010
2. **Agent installation** (`--agent`) — the agent binary is pre-installed in `PATH`.
11-
3. **Agent settings**
11+
3. **Agent settings** (`--with-agent-settings`) — only included when this flag is set.
1212
- **User settings** — settings files are pre-populated with settings files provided by the user.
1313
- **Auto-onboarding** — settings files are updated to skip onboarding steps (choose theme, trust directory, etc).
1414
- **Skills** — skills are copied in the agent's skills directory.
@@ -174,6 +174,8 @@ openshell-image-builder --agent opencode myimage:latest
174174

175175
## Agent settings
176176

177+
Pass `--with-agent-settings` to generate and include agent settings in the image. Without this flag, no settings files are written and no auto-configuration is applied — the image contains only the agent binary.
178+
177179
You can pre-populate the sandbox home directory with settings files specific to an agent. Place the files under:
178180

179181
```
@@ -191,7 +193,7 @@ mkdir -p ~/.config/openshell-image-builder/agents/claude/.claude
191193
cp ~/.claude/settings.json \
192194
~/.config/openshell-image-builder/agents/claude/.claude/settings.json
193195

194-
openshell-image-builder --agent claude myimage:latest
196+
openshell-image-builder --agent claude --with-agent-settings myimage:latest
195197
```
196198

197199
The file will be present at `/sandbox/.claude/settings.json` in the image.
@@ -203,14 +205,14 @@ mkdir -p ~/.config/openshell-image-builder/agents/opencode/.config/opencode
203205
cp ~/.config/opencode/config.json \
204206
~/.config/openshell-image-builder/agents/opencode/.config/opencode/config.json
205207

206-
openshell-image-builder --agent opencode myimage:latest
208+
openshell-image-builder --agent opencode --with-agent-settings myimage:latest
207209
```
208210

209211
The file will be present at `/sandbox/.config/opencode/config.json` in the image.
210212

211213
### Automatic configuration — Claude Code
212214

213-
When `--agent claude` is used, the builder automatically creates or updates `/sandbox/.claude.json` with the following settings to skip the interactive onboarding dialogs that would otherwise appear on first launch:
215+
When `--agent claude --with-agent-settings` is used, the builder automatically creates or updates `/sandbox/.claude.json` with the following settings to skip the interactive onboarding dialogs that would otherwise appear on first launch:
214216

215217
- `hasCompletedOnboarding: true` — marks the setup wizard as complete.
216218
- `projects["/sandbox"].hasTrustDialogAccepted: true` — pre-accepts the workspace trust prompt for the sandbox home directory.
@@ -306,7 +308,7 @@ The model string is passed through as-is — use whatever identifier your agent
306308

307309
### Automatic configuration — OpenCode + Ollama
308310

309-
When `--agent opencode --inference ollama` is used, the builder automatically writes `/sandbox/.config/opencode/config.json` to configure OpenCode's Ollama provider:
311+
When `--agent opencode --inference ollama --with-agent-settings` is used, the builder automatically writes `/sandbox/.config/opencode/config.json` to configure OpenCode's Ollama provider:
310312

311313
```json
312314
{
@@ -506,6 +508,7 @@ openshell-image-builder [OPTIONS] <TAG>
506508
| `--model <MODEL>` | Default model for the agent to use (see [Default model](#default-model---model)) |
507509
| `--with-workspace-config` | Read `.kaiden/workspace.json` and apply its features, skills, and network rules |
508510
| `--with-policy` | Include OpenShell sandbox policy (`/etc/openshell/policy.yaml`) in the image |
511+
| `--with-agent-settings` | Generate and include agent settings in the image (see [Agent settings](#agent-settings)) |
509512
| `-v` / `-vv` | Increase log verbosity (info / debug) |
510513

511514
## Examples
@@ -517,6 +520,7 @@ $ openshell-image-builder \
517520
--agent claude \
518521
--inference anthropic \
519522
--model claude-sonnet-4-6 \
523+
--with-agent-settings \
520524
sandbox_image:claude_anthropic
521525

522526
$ openshell provider create \
@@ -550,6 +554,7 @@ $ openshell-image-builder \
550554
--agent opencode \
551555
--inference anthropic \
552556
--model claude-sonnet-4-6 \
557+
--with-agent-settings \
553558
sandbox_image:opencode_anthropic
554559

555560
$ openshell provider create \
@@ -583,6 +588,7 @@ $ openshell-image-builder \
583588
--agent claude \
584589
--inference vertexai \
585590
--model claude-sonnet-4-6 \
591+
--with-agent-settings \
586592
sandbox_image:claude_vertexai
587593

588594
# change value of ANTHROPIC_VERTEX_PROJECT_ID and CLOUD_ML_REGION
@@ -608,6 +614,7 @@ $ openshell-image-builder \
608614
--agent opencode \
609615
--inference ollama \
610616
--model qwen3-coder:30b \
617+
--with-agent-settings \
611618
sandbox_image:opencode_ollama
612619

613620
$ openshell sandbox create \
@@ -634,6 +641,7 @@ $ openshell-image-builder \
634641
--agent opencode \
635642
--inference openai \
636643
--model gpt-4o \
644+
--with-agent-settings \
637645
sandbox_image:opencode_openai
638646

639647
$ openshell provider create \
@@ -668,6 +676,7 @@ $ openshell-image-builder \
668676
--inference openai \
669677
--endpoint https://my-openai-proxy.example.com/v1 \
670678
--model gpt-4o \
679+
--with-agent-settings \
671680
sandbox_image:opencode_openai_custom
672681
```
673682

@@ -678,6 +687,7 @@ $ openshell-image-builder \
678687
--agent opencode \
679688
--inference vertexai \
680689
--model claude-sonnet-4-6 \
690+
--with-agent-settings \
681691
sandbox_image:opencode_vertexai
682692

683693
# change value of GOOGLE_CLOUD_PROJECT and VERTEX_LOCATION

src/main.rs

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ struct Cli {
6868
with_workspace_config: bool,
6969
#[arg(long, help = "Include OpenShell sandbox policy in the image")]
7070
with_policy: bool,
71+
#[arg(long, help = "Generate and include agent settings in the image")]
72+
with_agent_settings: bool,
7173
}
7274

7375
fn main() {
@@ -89,6 +91,7 @@ fn main() {
8991
cli.endpoint.as_deref(),
9092
cli.model.as_deref(),
9193
cli.with_policy,
94+
cli.with_agent_settings,
9295
&build::PodmanRunner,
9396
) {
9497
eprintln!("Error: {e}");
@@ -106,6 +109,7 @@ fn run(
106109
endpoint: Option<&str>,
107110
model: Option<&str>,
108111
with_policy: bool,
112+
with_agent_settings: bool,
109113
runner: &dyn build::Runner,
110114
) -> Result<(), Box<dyn std::error::Error>> {
111115
if endpoint.is_some() && inference_kind == Some(inference::InferenceKind::VertexAi) {
@@ -132,16 +136,20 @@ fn run(
132136
.prefix("openshell-image-builder")
133137
.tempdir()?;
134138
let features = feature::stage_all(workspace.as_ref(), context_dir.path())?;
135-
let has_agent_settings = if let Some(a) = agent.as_deref() {
136-
let settings_dir = config::agent_settings_dir(config_path.as_deref(), a.id())?;
137-
stage_agent_settings(
138-
a,
139-
settings_dir.as_deref(),
140-
inference_kind.as_ref(),
141-
endpoint,
142-
model,
143-
context_dir.path(),
144-
)?
139+
let has_agent_settings = if with_agent_settings {
140+
if let Some(a) = agent.as_deref() {
141+
let settings_dir = config::agent_settings_dir(config_path.as_deref(), a.id())?;
142+
stage_agent_settings(
143+
a,
144+
settings_dir.as_deref(),
145+
inference_kind.as_ref(),
146+
endpoint,
147+
model,
148+
context_dir.path(),
149+
)?
150+
} else {
151+
false
152+
}
145153
} else {
146154
false
147155
};
@@ -815,6 +823,7 @@ mod tests {
815823
None,
816824
None,
817825
false,
826+
false,
818827
&FakeRunner(0),
819828
);
820829
assert!(result.is_ok(), "expected Ok, got {result:?}");
@@ -832,6 +841,7 @@ mod tests {
832841
None,
833842
None,
834843
false,
844+
false,
835845
&FakeRunner(0),
836846
);
837847
assert!(result.is_ok(), "expected Ok, got {result:?}");
@@ -849,6 +859,7 @@ mod tests {
849859
None,
850860
None,
851861
false,
862+
false,
852863
&FakeRunner(0),
853864
);
854865
assert!(result.is_ok(), "expected Ok, got {result:?}");
@@ -866,6 +877,7 @@ mod tests {
866877
None,
867878
None,
868879
false,
880+
false,
869881
&FakeRunner(0),
870882
);
871883
assert!(result.is_err());
@@ -889,6 +901,7 @@ mod tests {
889901
None,
890902
None,
891903
false,
904+
false,
892905
&FakeRunner(1),
893906
);
894907
assert!(result.is_err());
@@ -906,6 +919,7 @@ mod tests {
906919
Some("https://my-vertex-proxy.example.com"),
907920
None,
908921
false,
922+
false,
909923
&FakeRunner(0),
910924
);
911925
assert!(result.is_err());
@@ -929,6 +943,7 @@ mod tests {
929943
None,
930944
Some("claude-opus-4-5"),
931945
false,
946+
false,
932947
&FakeRunner(0),
933948
);
934949
assert!(result.is_ok(), "expected Ok, got {result:?}");
@@ -1088,6 +1103,25 @@ mod tests {
10881103
None,
10891104
None,
10901105
true,
1106+
false,
1107+
&FakeRunner(0),
1108+
);
1109+
assert!(result.is_ok(), "expected Ok, got {result:?}");
1110+
}
1111+
1112+
#[test]
1113+
fn run_with_agent_settings_and_claude_agent_succeeds() {
1114+
let tmp = tempfile::tempdir().unwrap();
1115+
let result = run(
1116+
"test:latest",
1117+
Some(tmp.path().to_path_buf()),
1118+
false,
1119+
Some(agent::AgentKind::Claude),
1120+
None,
1121+
None,
1122+
None,
1123+
false,
1124+
true,
10911125
&FakeRunner(0),
10921126
);
10931127
assert!(result.is_ok(), "expected Ok, got {result:?}");
@@ -1105,6 +1139,7 @@ mod tests {
11051139
None,
11061140
None,
11071141
false,
1142+
false,
11081143
&FakeRunner(0),
11091144
);
11101145
assert!(result.is_err());

0 commit comments

Comments
 (0)