drizzy-agent 0.3.1 → 0.4.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/dist/cli/index.js CHANGED
@@ -3421,6 +3421,10 @@ var init_add_plugin_to_opencode_config = __esm(() => {
3421
3421
  init_plugin_name_with_version();
3422
3422
  });
3423
3423
 
3424
+ // src/shared/install-defaults-contract.ts
3425
+ var INSTALL_DEFAULTS_SNAPSHOT_VERSION = 1;
3426
+ var init_install_defaults_contract = () => {};
3427
+
3424
3428
  // src/cli/model-fallback-requirements.ts
3425
3429
  var CLI_AGENT_MODEL_REQUIREMENTS, CLI_CATEGORY_MODEL_REQUIREMENTS;
3426
3430
  var init_model_fallback_requirements = __esm(() => {
@@ -3711,65 +3715,6 @@ var init_model_fallback_requirements = __esm(() => {
3711
3715
  };
3712
3716
  });
3713
3717
 
3714
- // src/cli/openai-only-model-catalog.ts
3715
- function isOpenAiOnlyAvailability(availability) {
3716
- return availability.native.openai && !availability.native.claude && !availability.native.gemini && !availability.opencodeZen && !availability.copilot && !availability.zai && !availability.kimiForCoding;
3717
- }
3718
- function applyOpenAiOnlyModelCatalog(config) {
3719
- return {
3720
- ...config,
3721
- agents: {
3722
- ...config.agents,
3723
- ...OPENAI_ONLY_AGENT_OVERRIDES
3724
- },
3725
- categories: {
3726
- ...config.categories,
3727
- ...OPENAI_ONLY_CATEGORY_OVERRIDES
3728
- }
3729
- };
3730
- }
3731
- var OPENAI_ONLY_AGENT_OVERRIDES, OPENAI_ONLY_CATEGORY_OVERRIDES;
3732
- var init_openai_only_model_catalog = __esm(() => {
3733
- OPENAI_ONLY_AGENT_OVERRIDES = {
3734
- explore: { model: "openai/gpt-5.4", variant: "medium" },
3735
- librarian: { model: "openai/gpt-5.4", variant: "medium" }
3736
- };
3737
- OPENAI_ONLY_CATEGORY_OVERRIDES = {
3738
- artistry: { model: "openai/gpt-5.4", variant: "xhigh" },
3739
- quick: { model: "openai/gpt-5.4", variant: "low" },
3740
- "visual-engineering": { model: "openai/gpt-5.4", variant: "high" },
3741
- writing: { model: "openai/gpt-5.4", variant: "medium" }
3742
- };
3743
- });
3744
-
3745
- // src/cli/provider-availability.ts
3746
- function toProviderAvailability(config) {
3747
- return {
3748
- native: {
3749
- claude: config.hasClaude,
3750
- openai: config.hasOpenAI,
3751
- gemini: config.hasGemini
3752
- },
3753
- opencodeZen: config.hasOpencodeZen,
3754
- copilot: config.hasCopilot,
3755
- zai: config.hasZaiCodingPlan,
3756
- kimiForCoding: config.hasKimiForCoding,
3757
- isMaxPlan: config.isMax20
3758
- };
3759
- }
3760
- function isProviderAvailable(provider, availability) {
3761
- const mapping = {
3762
- anthropic: availability.native.claude,
3763
- openai: availability.native.openai,
3764
- google: availability.native.gemini,
3765
- "github-copilot": availability.copilot,
3766
- opencode: availability.opencodeZen,
3767
- "zai-coding-plan": availability.zai,
3768
- "kimi-for-coding": availability.kimiForCoding
3769
- };
3770
- return mapping[provider] ?? false;
3771
- }
3772
-
3773
3718
  // src/shared/provider-model-id-transform.ts
3774
3719
  function transformModelForProvider(provider, model) {
3775
3720
  if (provider === "github-copilot") {
@@ -3816,101 +3761,150 @@ function isRequiredProviderAvailable(requiredProviders, availability) {
3816
3761
  }
3817
3762
  var init_fallback_chain_resolution = __esm(() => {
3818
3763
  init_model_fallback_requirements();
3764
+ init_provider_availability();
3819
3765
  init_provider_model_id_transform();
3820
3766
  });
3821
3767
 
3822
- // src/cli/model-fallback.ts
3823
- function generateModelConfig(config) {
3824
- const avail = toProviderAvailability(config);
3825
- const hasAnyProvider = avail.native.claude || avail.native.openai || avail.native.gemini || avail.opencodeZen || avail.copilot || avail.zai || avail.kimiForCoding;
3826
- if (!hasAnyProvider) {
3768
+ // src/shared/computed-install-defaults.ts
3769
+ function toComputedProviderAvailability(providers, options) {
3770
+ return {
3771
+ native: {
3772
+ claude: providers.claude !== "no",
3773
+ openai: providers.openai,
3774
+ gemini: providers.gemini
3775
+ },
3776
+ opencodeZen: providers.opencode_zen,
3777
+ copilot: providers.copilot,
3778
+ zai: providers.zai_coding_plan,
3779
+ kimiForCoding: providers.kimi_for_coding,
3780
+ isMaxPlan: options?.isMaxPlan ?? providers.claude === "max20"
3781
+ };
3782
+ }
3783
+ function computeDefaultsFromProviders(providers, options) {
3784
+ const availability = toComputedProviderAvailability(providers, options);
3785
+ if (!hasAnyProvider(availability)) {
3827
3786
  return {
3828
- $schema: SCHEMA_URL,
3829
- agents: Object.fromEntries(Object.entries(CLI_AGENT_MODEL_REQUIREMENTS).filter(([role, req]) => !(role === "coder" && req.requiresAnyModel)).map(([role]) => [role, { model: ULTIMATE_FALLBACK }])),
3830
- categories: Object.fromEntries(Object.keys(CLI_CATEGORY_MODEL_REQUIREMENTS).map((cat) => [cat, { model: ULTIMATE_FALLBACK }]))
3787
+ agents: Object.fromEntries(Object.entries(CLI_AGENT_MODEL_REQUIREMENTS).filter(([role, requirement]) => !(role === "coder" && requirement.requiresAnyModel)).map(([role]) => [role, { model: ULTIMATE_FALLBACK }])),
3788
+ categories: Object.fromEntries(Object.keys(CLI_CATEGORY_MODEL_REQUIREMENTS).map((category) => [category, { model: ULTIMATE_FALLBACK }]))
3831
3789
  };
3832
3790
  }
3833
3791
  const agents = {};
3834
3792
  const categories = {};
3835
- for (const [role, req] of Object.entries(CLI_AGENT_MODEL_REQUIREMENTS)) {
3836
- if (role === "librarian" && avail.zai) {
3793
+ for (const [role, requirement] of Object.entries(CLI_AGENT_MODEL_REQUIREMENTS)) {
3794
+ if (role === "librarian" && availability.zai) {
3837
3795
  agents[role] = { model: ZAI_MODEL };
3838
3796
  continue;
3839
3797
  }
3840
3798
  if (role === "explore") {
3841
- if (avail.native.claude) {
3842
- agents[role] = { model: "anthropic/claude-haiku-4-5" };
3843
- } else if (avail.opencodeZen) {
3844
- agents[role] = { model: "opencode/claude-haiku-4-5" };
3845
- } else if (avail.copilot) {
3846
- agents[role] = { model: "github-copilot/gpt-5-mini" };
3847
- } else {
3848
- agents[role] = { model: "opencode/gpt-5-nano" };
3849
- }
3799
+ agents[role] = resolveExploreAgent(availability);
3850
3800
  continue;
3851
3801
  }
3852
3802
  if (role === "coder") {
3853
3803
  const fallbackChain = getCoderFallbackChain();
3854
- if (req.requiresAnyModel && !isAnyFallbackEntryAvailable(fallbackChain, avail)) {
3804
+ if (requirement.requiresAnyModel && !isAnyFallbackEntryAvailable(fallbackChain, availability)) {
3855
3805
  continue;
3856
3806
  }
3857
- const resolved2 = resolveModelFromChain(fallbackChain, avail);
3858
- if (resolved2) {
3859
- const variant = resolved2.variant ?? req.variant;
3860
- agents[role] = variant ? { model: resolved2.model, variant } : { model: resolved2.model };
3807
+ const resolvedCoder = resolveModelFromChain(fallbackChain, availability);
3808
+ if (resolvedCoder) {
3809
+ agents[role] = withVariant(resolvedCoder.model, resolvedCoder.variant ?? requirement.variant);
3861
3810
  }
3862
3811
  continue;
3863
3812
  }
3864
- if (req.requiresModel && !isRequiredModelAvailable(req.requiresModel, req.fallbackChain, avail)) {
3865
- continue;
3866
- }
3867
- if (req.requiresProvider && !isRequiredProviderAvailable(req.requiresProvider, avail)) {
3868
- continue;
3869
- }
3870
- const resolved = resolveModelFromChain(req.fallbackChain, avail);
3813
+ const resolved = resolveRequirementDefault(requirement, availability);
3871
3814
  if (resolved) {
3872
- const variant = resolved.variant ?? req.variant;
3873
- agents[role] = variant ? { model: resolved.model, variant } : { model: resolved.model };
3874
- } else {
3875
- agents[role] = { model: ULTIMATE_FALLBACK };
3815
+ agents[role] = resolved;
3876
3816
  }
3877
3817
  }
3878
- for (const [cat, req] of Object.entries(CLI_CATEGORY_MODEL_REQUIREMENTS)) {
3879
- const fallbackChain = cat === "unspecified-high" && !avail.isMaxPlan ? CLI_CATEGORY_MODEL_REQUIREMENTS["unspecified-low"].fallbackChain : req.fallbackChain;
3880
- if (req.requiresModel && !isRequiredModelAvailable(req.requiresModel, req.fallbackChain, avail)) {
3881
- continue;
3882
- }
3883
- if (req.requiresProvider && !isRequiredProviderAvailable(req.requiresProvider, avail)) {
3884
- continue;
3885
- }
3886
- const resolved = resolveModelFromChain(fallbackChain, avail);
3818
+ for (const [category, requirement] of Object.entries(CLI_CATEGORY_MODEL_REQUIREMENTS)) {
3819
+ const fallbackChain = category === "unspecified-high" && !availability.isMaxPlan ? CLI_CATEGORY_MODEL_REQUIREMENTS["unspecified-low"].fallbackChain : requirement.fallbackChain;
3820
+ const resolved = resolveRequirementDefault(requirement, availability, fallbackChain);
3887
3821
  if (resolved) {
3888
- const variant = resolved.variant ?? req.variant;
3889
- categories[cat] = variant ? { model: resolved.model, variant } : { model: resolved.model };
3890
- } else {
3891
- categories[cat] = { model: ULTIMATE_FALLBACK };
3822
+ categories[category] = resolved;
3892
3823
  }
3893
3824
  }
3894
- const generatedConfig = {
3895
- $schema: SCHEMA_URL,
3896
- agents,
3897
- categories
3898
- };
3899
- return isOpenAiOnlyAvailability(avail) ? applyOpenAiOnlyModelCatalog(generatedConfig) : generatedConfig;
3825
+ return { agents, categories };
3900
3826
  }
3901
- var ZAI_MODEL = "zai-coding-plan/glm-4.7", ULTIMATE_FALLBACK = "opencode/glm-4.7-free", SCHEMA_URL = "https://raw.githubusercontent.com/AndreDalwin/DrizzyAgent/dev/assets/drizzy-agent.schema.json";
3902
- var init_model_fallback = __esm(() => {
3903
- init_model_fallback_requirements();
3904
- init_openai_only_model_catalog();
3827
+ function hasAnyProvider(availability) {
3828
+ return availability.native.claude || availability.native.openai || availability.native.gemini || availability.opencodeZen || availability.copilot || availability.zai || availability.kimiForCoding;
3829
+ }
3830
+ function resolveExploreAgent(availability) {
3831
+ if (availability.native.claude) {
3832
+ return { model: "anthropic/claude-haiku-4-5" };
3833
+ }
3834
+ if (availability.opencodeZen) {
3835
+ return { model: "opencode/claude-haiku-4-5" };
3836
+ }
3837
+ if (availability.copilot) {
3838
+ return { model: "github-copilot/gpt-5-mini" };
3839
+ }
3840
+ return { model: "opencode/gpt-5-nano" };
3841
+ }
3842
+ function resolveRequirementDefault(requirement, availability, fallbackChain = requirement.fallbackChain) {
3843
+ if (requirement.requiresModel && !isRequiredModelAvailable(requirement.requiresModel, requirement.fallbackChain, availability)) {
3844
+ return;
3845
+ }
3846
+ if (requirement.requiresProvider && !isRequiredProviderAvailable(requirement.requiresProvider, availability)) {
3847
+ return;
3848
+ }
3849
+ const resolved = resolveModelFromChain(fallbackChain, availability);
3850
+ if (!resolved) {
3851
+ return { model: ULTIMATE_FALLBACK };
3852
+ }
3853
+ return withVariant(resolved.model, resolved.variant ?? requirement.variant);
3854
+ }
3855
+ function withVariant(model, variant) {
3856
+ return variant ? { model, variant } : { model };
3857
+ }
3858
+ var ZAI_MODEL = "zai-coding-plan/glm-4.7", ULTIMATE_FALLBACK = "opencode/glm-4.7-free";
3859
+ var init_computed_install_defaults = __esm(() => {
3905
3860
  init_fallback_chain_resolution();
3861
+ init_model_fallback_requirements();
3862
+ });
3863
+
3864
+ // src/cli/provider-availability.ts
3865
+ function toInstallDefaultsProviders(config) {
3866
+ return {
3867
+ claude: config.hasClaude ? config.isMax20 ? "max20" : "yes" : "no",
3868
+ openai: config.hasOpenAI,
3869
+ gemini: config.hasGemini,
3870
+ copilot: config.hasCopilot,
3871
+ opencode_zen: config.hasOpencodeZen,
3872
+ zai_coding_plan: config.hasZaiCodingPlan,
3873
+ kimi_for_coding: config.hasKimiForCoding
3874
+ };
3875
+ }
3876
+ function isProviderAvailable(provider, availability) {
3877
+ const mapping = {
3878
+ anthropic: availability.native.claude,
3879
+ openai: availability.native.openai,
3880
+ google: availability.native.gemini,
3881
+ "github-copilot": availability.copilot,
3882
+ opencode: availability.opencodeZen,
3883
+ "zai-coding-plan": availability.zai,
3884
+ "kimi-for-coding": availability.kimiForCoding
3885
+ };
3886
+ return mapping[provider] ?? false;
3887
+ }
3888
+ var init_provider_availability = __esm(() => {
3889
+ init_computed_install_defaults();
3906
3890
  });
3907
3891
 
3908
3892
  // src/cli/config-manager/generate-drizzy-config.ts
3909
3893
  function generateDrizzyConfig(installConfig) {
3910
- return generateModelConfig(installConfig);
3894
+ const providers = toInstallDefaultsProviders(installConfig);
3895
+ const installDefaults = {
3896
+ snapshot_version: INSTALL_DEFAULTS_SNAPSHOT_VERSION,
3897
+ providers
3898
+ };
3899
+ const schemaUrl = "https://raw.githubusercontent.com/AndreDalwin/DrizzyAgent/dev/assets/drizzy-agent.schema.json";
3900
+ return {
3901
+ $schema: schemaUrl,
3902
+ _install_defaults: installDefaults
3903
+ };
3911
3904
  }
3912
3905
  var init_generate_drizzy_config = __esm(() => {
3913
- init_model_fallback();
3906
+ init_install_defaults_contract();
3907
+ init_provider_availability();
3914
3908
  });
3915
3909
 
3916
3910
  // src/cli/config-manager/write-drizzy-config.ts
@@ -3918,19 +3912,11 @@ import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as s
3918
3912
  function isEmptyOrWhitespace2(content) {
3919
3913
  return content.trim().length === 0;
3920
3914
  }
3921
- function mergeGeneratedConfigWithExisting(generated, existing) {
3922
- const result = { ...existing };
3923
- for (const [key, generatedValue] of Object.entries(generated)) {
3924
- if (key === "__proto__" || key === "constructor" || key === "prototype")
3925
- continue;
3926
- const existingValue = existing[key];
3927
- if (generatedValue !== null && typeof generatedValue === "object" && !Array.isArray(generatedValue) && existingValue !== null && typeof existingValue === "object" && !Array.isArray(existingValue)) {
3928
- result[key] = mergeGeneratedConfigWithExisting(generatedValue, existingValue);
3929
- continue;
3930
- }
3931
- result[key] = generatedValue;
3932
- }
3933
- return result;
3915
+ function mergeSnapshotConfigWithExisting(generated, existing) {
3916
+ return {
3917
+ ...existing,
3918
+ ...generated
3919
+ };
3934
3920
  }
3935
3921
  function writeDrizzyConfig(installConfig) {
3936
3922
  try {
@@ -3943,13 +3929,12 @@ function writeDrizzyConfig(installConfig) {
3943
3929
  };
3944
3930
  }
3945
3931
  const drizzyConfigPath = getDrizzyConfigPath();
3946
- const existingConfigPath = drizzyConfigPath;
3947
3932
  try {
3948
3933
  const newConfig = generateDrizzyConfig(installConfig);
3949
- if (existsSync5(existingConfigPath)) {
3934
+ if (existsSync5(drizzyConfigPath)) {
3950
3935
  try {
3951
- const stat = statSync2(existingConfigPath);
3952
- const content = readFileSync4(existingConfigPath, "utf-8");
3936
+ const stat = statSync2(drizzyConfigPath);
3937
+ const content = readFileSync4(drizzyConfigPath, "utf-8");
3953
3938
  if (stat.size === 0 || isEmptyOrWhitespace2(content)) {
3954
3939
  writeFileSync2(drizzyConfigPath, JSON.stringify(newConfig, null, 2) + `
3955
3940
  `);
@@ -3961,7 +3946,7 @@ function writeDrizzyConfig(installConfig) {
3961
3946
  `);
3962
3947
  return { success: true, configPath: drizzyConfigPath };
3963
3948
  }
3964
- const merged = mergeGeneratedConfigWithExisting(newConfig, existing);
3949
+ const merged = mergeSnapshotConfigWithExisting(newConfig, existing);
3965
3950
  writeFileSync2(drizzyConfigPath, JSON.stringify(merged, null, 2) + `
3966
3951
  `);
3967
3952
  } catch (parseErr) {
@@ -4095,70 +4080,41 @@ function isRecord(value) {
4095
4080
  return typeof value === "object" && value !== null && !Array.isArray(value);
4096
4081
  }
4097
4082
  function detectProvidersFromDrizzyConfig(deps) {
4098
- const drizzyConfigPath = deps.getDrizzyConfigPath();
4099
- const configPath = existsSync6(drizzyConfigPath) ? drizzyConfigPath : join2(deps.getConfigDir(), "drizzy-agent.json");
4100
- if (!existsSync6(configPath)) {
4101
- return {
4102
- hasClaude: false,
4103
- isMax20: false,
4104
- hasOpenAI: true,
4105
- hasGemini: false,
4106
- hasCopilot: false,
4107
- hasOpencodeZen: true,
4108
- hasZaiCodingPlan: false,
4109
- hasKimiForCoding: false
4110
- };
4111
- }
4083
+ const configPath = existsSync6(deps.getDrizzyConfigPath()) && deps.getDrizzyConfigPath() ? deps.getDrizzyConfigPath() : join2(deps.getConfigDir(), "drizzy-agent.json");
4084
+ if (!existsSync6(configPath))
4085
+ return DEFAULT_PROVIDER_DETECTION;
4112
4086
  try {
4113
- const content = readFileSync5(configPath, "utf-8");
4114
- const drizzyConfig = deps.parseJsonc(content);
4115
- if (!isRecord(drizzyConfig)) {
4087
+ const drizzyConfig = deps.parseJsonc(readFileSync5(configPath, "utf-8"));
4088
+ if (!isRecord(drizzyConfig))
4089
+ return DEFAULT_PROVIDER_DETECTION;
4090
+ const installDefaults = drizzyConfig._install_defaults;
4091
+ if (isRecord(installDefaults) && isRecord(installDefaults.providers)) {
4092
+ const p = installDefaults.providers;
4116
4093
  return {
4117
- hasClaude: false,
4118
- isMax20: false,
4119
- hasOpenAI: true,
4120
- hasGemini: false,
4121
- hasCopilot: false,
4122
- hasOpencodeZen: true,
4123
- hasZaiCodingPlan: false,
4124
- hasKimiForCoding: false
4094
+ hasClaude: p.claude !== "no",
4095
+ isMax20: p.claude === "max20",
4096
+ hasOpenAI: p.openai === true,
4097
+ hasGemini: p.gemini === true,
4098
+ hasCopilot: p.copilot === true,
4099
+ hasOpencodeZen: p.opencode_zen === true,
4100
+ hasZaiCodingPlan: p.zai_coding_plan === true,
4101
+ hasKimiForCoding: p.kimi_for_coding === true
4125
4102
  };
4126
4103
  }
4127
- const configStr = JSON.stringify(drizzyConfig);
4128
- const hasClaude = configStr.includes('"anthropic/');
4129
- const hasOpenAI = configStr.includes('"openai/');
4130
- const hasGemini = configStr.includes('"google/');
4131
- const hasCopilot = configStr.includes('"github-copilot/');
4132
- const hasOpencodeZen = configStr.includes('"opencode/');
4133
- const hasZaiCodingPlan = configStr.includes('"zai-coding-plan/');
4134
- const hasKimiForCoding = configStr.includes('"kimi-for-coding/');
4135
- const categories = drizzyConfig.categories;
4136
- const unspecifiedHigh = isRecord(categories) ? categories["unspecified-high"] : undefined;
4137
- const isMax20 = hasClaude && isRecord(unspecifiedHigh) && unspecifiedHigh.model === "anthropic/claude-opus-4-6" && unspecifiedHigh.variant === "max";
4138
- return {
4139
- hasClaude,
4140
- isMax20,
4141
- hasOpenAI,
4142
- hasGemini,
4143
- hasCopilot,
4144
- hasOpencodeZen,
4145
- hasZaiCodingPlan,
4146
- hasKimiForCoding
4147
- };
4104
+ return DEFAULT_PROVIDER_DETECTION;
4148
4105
  } catch {
4149
- return {
4150
- hasClaude: false,
4151
- isMax20: false,
4152
- hasOpenAI: true,
4153
- hasGemini: false,
4154
- hasCopilot: false,
4155
- hasOpencodeZen: true,
4156
- hasZaiCodingPlan: false,
4157
- hasKimiForCoding: false
4158
- };
4106
+ return DEFAULT_PROVIDER_DETECTION;
4159
4107
  }
4160
4108
  }
4161
4109
  function detectCurrentConfig(deps = defaultDependencies) {
4110
+ const { format: format2, path } = deps.detectConfigFormat();
4111
+ if (format2 === "none") {
4112
+ return { isInstalled: false, ...DEFAULT_PROVIDER_DETECTION };
4113
+ }
4114
+ const parseResult = deps.parseOpenCodeConfigFileWithError(path);
4115
+ if (!parseResult.config) {
4116
+ return { isInstalled: false, ...DEFAULT_PROVIDER_DETECTION };
4117
+ }
4162
4118
  const result = {
4163
4119
  isInstalled: false,
4164
4120
  hasClaude: false,
@@ -4170,14 +4126,6 @@ function detectCurrentConfig(deps = defaultDependencies) {
4170
4126
  hasZaiCodingPlan: false,
4171
4127
  hasKimiForCoding: false
4172
4128
  };
4173
- const { format: format2, path } = deps.detectConfigFormat();
4174
- if (format2 === "none") {
4175
- return result;
4176
- }
4177
- const parseResult = deps.parseOpenCodeConfigFileWithError(path);
4178
- if (!parseResult.config) {
4179
- return result;
4180
- }
4181
4129
  const openCodeConfig = parseResult.config;
4182
4130
  const plugins = openCodeConfig.plugin ?? [];
4183
4131
  result.isInstalled = plugins.some((p) => p.startsWith("drizzy-agent"));
@@ -4205,7 +4153,7 @@ function detectCurrentConfig(deps = defaultDependencies) {
4205
4153
  result.hasKimiForCoding = hasKimiForCoding;
4206
4154
  return result;
4207
4155
  }
4208
- var defaultDependencies;
4156
+ var defaultDependencies, DEFAULT_PROVIDER_DETECTION;
4209
4157
  var init_detect_current_config = __esm(() => {
4210
4158
  init_jsonc_parser();
4211
4159
  init_config_context();
@@ -4218,6 +4166,16 @@ var init_detect_current_config = __esm(() => {
4218
4166
  parseJsonc,
4219
4167
  parseOpenCodeConfigFileWithError
4220
4168
  };
4169
+ DEFAULT_PROVIDER_DETECTION = {
4170
+ hasClaude: false,
4171
+ isMax20: false,
4172
+ hasOpenAI: true,
4173
+ hasGemini: false,
4174
+ hasCopilot: false,
4175
+ hasOpencodeZen: true,
4176
+ hasZaiCodingPlan: false,
4177
+ hasKimiForCoding: false
4178
+ };
4221
4179
  });
4222
4180
 
4223
4181
  // src/shared/data-path.ts
@@ -4700,6 +4658,7 @@ var init_model_availability = __esm(() => {
4700
4658
  init_data_path();
4701
4659
  init_connected_providers_cache();
4702
4660
  });
4661
+
4703
4662
  // src/hooks/auto-update-checker/constants.ts
4704
4663
  import * as path4 from "path";
4705
4664
  import * as os3 from "os";
@@ -5531,7 +5490,7 @@ var {
5531
5490
  // package.json
5532
5491
  var package_default = {
5533
5492
  name: "drizzy-agent",
5534
- version: "0.3.1",
5493
+ version: "0.4.0",
5535
5494
  description: "DrizzyAgent - AI agent plugin for OpenCode",
5536
5495
  main: "dist/index.js",
5537
5496
  types: "dist/index.d.ts",
@@ -5607,17 +5566,17 @@ var package_default = {
5607
5566
  typescript: "^5.7.3"
5608
5567
  },
5609
5568
  optionalDependencies: {
5610
- "drizzy-agent-darwin-arm64": "0.3.1",
5611
- "drizzy-agent-darwin-x64": "0.3.1",
5612
- "drizzy-agent-darwin-x64-baseline": "0.3.1",
5613
- "drizzy-agent-linux-arm64": "0.3.1",
5614
- "drizzy-agent-linux-arm64-musl": "0.3.1",
5615
- "drizzy-agent-linux-x64": "0.3.1",
5616
- "drizzy-agent-linux-x64-baseline": "0.3.1",
5617
- "drizzy-agent-linux-x64-musl": "0.3.1",
5618
- "drizzy-agent-linux-x64-musl-baseline": "0.3.1",
5619
- "drizzy-agent-windows-x64": "0.3.1",
5620
- "drizzy-agent-windows-x64-baseline": "0.3.1"
5569
+ "drizzy-agent-darwin-arm64": "0.4.0",
5570
+ "drizzy-agent-darwin-x64": "0.4.0",
5571
+ "drizzy-agent-darwin-x64-baseline": "0.4.0",
5572
+ "drizzy-agent-linux-arm64": "0.4.0",
5573
+ "drizzy-agent-linux-arm64-musl": "0.4.0",
5574
+ "drizzy-agent-linux-x64": "0.4.0",
5575
+ "drizzy-agent-linux-x64-baseline": "0.4.0",
5576
+ "drizzy-agent-linux-x64-musl": "0.4.0",
5577
+ "drizzy-agent-linux-x64-musl-baseline": "0.4.0",
5578
+ "drizzy-agent-windows-x64": "0.4.0",
5579
+ "drizzy-agent-windows-x64-baseline": "0.4.0"
5621
5580
  },
5622
5581
  overrides: {
5623
5582
  "@opencode-ai/sdk": "^1.2.24"
@@ -7440,6 +7399,75 @@ async function processEvents(ctx, stream, state) {
7440
7399
  import * as fs3 from "fs";
7441
7400
  import * as path3 from "path";
7442
7401
 
7402
+ // src/cli/openai-only-model-catalog.ts
7403
+ var OPENAI_ONLY_AGENT_OVERRIDES = {
7404
+ explore: { model: "openai/gpt-5.4", variant: "medium" },
7405
+ librarian: { model: "openai/gpt-5.4", variant: "medium" }
7406
+ };
7407
+ var OPENAI_ONLY_CATEGORY_OVERRIDES = {
7408
+ artistry: { model: "openai/gpt-5.4", variant: "xhigh" },
7409
+ quick: { model: "openai/gpt-5.4", variant: "low" },
7410
+ "visual-engineering": { model: "openai/gpt-5.4", variant: "high" },
7411
+ writing: { model: "openai/gpt-5.4", variant: "medium" }
7412
+ };
7413
+ function isOpenAiOnlyAvailability(availability) {
7414
+ return availability.native.openai && !availability.native.claude && !availability.native.gemini && !availability.opencodeZen && !availability.copilot && !availability.zai && !availability.kimiForCoding;
7415
+ }
7416
+ function applyOpenAiOnlyModelCatalog(config) {
7417
+ return {
7418
+ ...config,
7419
+ agents: {
7420
+ ...config.agents,
7421
+ ...OPENAI_ONLY_AGENT_OVERRIDES
7422
+ },
7423
+ categories: {
7424
+ ...config.categories,
7425
+ ...OPENAI_ONLY_CATEGORY_OVERRIDES
7426
+ }
7427
+ };
7428
+ }
7429
+
7430
+ // src/cli/model-fallback.ts
7431
+ init_computed_install_defaults();
7432
+ init_provider_availability();
7433
+ var SCHEMA_URL = "https://raw.githubusercontent.com/AndreDalwin/DrizzyAgent/dev/assets/drizzy-agent.schema.json";
7434
+ function generateModelConfig(config) {
7435
+ const providers = toInstallDefaultsProviders(config);
7436
+ const availability = toComputedProviderAvailability(providers, { isMaxPlan: config.isMax20 });
7437
+ const { agents, categories } = computeDefaultsFromProviders(providers, { isMaxPlan: config.isMax20 });
7438
+ const generatedConfig = {
7439
+ $schema: SCHEMA_URL,
7440
+ agents,
7441
+ categories
7442
+ };
7443
+ return isOpenAiOnlyAvailability(availability) ? applyOpenAiOnlyModelCatalog(generatedConfig) : generatedConfig;
7444
+ }
7445
+
7446
+ // src/computed-install-defaults.ts
7447
+ function toInstallConfig(snapshot) {
7448
+ return {
7449
+ hasClaude: snapshot.providers.claude !== "no",
7450
+ isMax20: snapshot.providers.claude === "max20",
7451
+ hasOpenAI: snapshot.providers.openai,
7452
+ hasGemini: snapshot.providers.gemini,
7453
+ hasCopilot: snapshot.providers.copilot,
7454
+ hasOpencodeZen: snapshot.providers.opencode_zen,
7455
+ hasZaiCodingPlan: snapshot.providers.zai_coding_plan,
7456
+ hasKimiForCoding: snapshot.providers.kimi_for_coding
7457
+ };
7458
+ }
7459
+ function synthesizeComputedDefaultsConfig(snapshot) {
7460
+ if (!snapshot) {
7461
+ return {};
7462
+ }
7463
+ const generatedConfig = generateModelConfig(toInstallConfig(snapshot));
7464
+ return {
7465
+ _install_defaults: snapshot,
7466
+ agents: generatedConfig.agents,
7467
+ categories: generatedConfig.categories
7468
+ };
7469
+ }
7470
+
7443
7471
  // node_modules/zod/v4/classic/external.js
7444
7472
  var exports_external = {};
7445
7473
  __export(exports_external, {
@@ -21272,6 +21300,22 @@ var HookNameSchema = exports_external.enum([
21272
21300
  "hashline-read-enhancer",
21273
21301
  "read-image-resizer"
21274
21302
  ]);
21303
+ // src/config/schema/install-defaults.ts
21304
+ init_install_defaults_contract();
21305
+ var InstallDefaultsClaudeProviderSchema = exports_external.enum(["no", "yes", "max20"]);
21306
+ var InstallDefaultsProvidersSchema = exports_external.object({
21307
+ claude: InstallDefaultsClaudeProviderSchema,
21308
+ openai: exports_external.boolean(),
21309
+ gemini: exports_external.boolean(),
21310
+ copilot: exports_external.boolean(),
21311
+ opencode_zen: exports_external.boolean(),
21312
+ zai_coding_plan: exports_external.boolean(),
21313
+ kimi_for_coding: exports_external.boolean()
21314
+ }).strict();
21315
+ var InstallDefaultsSnapshotSchema = exports_external.object({
21316
+ snapshot_version: exports_external.literal(INSTALL_DEFAULTS_SNAPSHOT_VERSION),
21317
+ providers: InstallDefaultsProvidersSchema
21318
+ }).strict();
21275
21319
  // src/config/schema/notification.ts
21276
21320
  var NotificationConfigSchema = exports_external.object({
21277
21321
  force_enable: exports_external.boolean().optional()
@@ -21408,6 +21452,7 @@ var DrizzyAgentConfigSchema = exports_external.object({
21408
21452
  tmux: TmuxConfigSchema.optional(),
21409
21453
  coder: CoderConfigSchema.optional(),
21410
21454
  start_work: StartWorkConfigSchema.optional(),
21455
+ _install_defaults: InstallDefaultsSnapshotSchema.optional(),
21411
21456
  _migrations: exports_external.array(exports_external.string()).optional()
21412
21457
  });
21413
21458
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -24272,7 +24317,8 @@ function migrateModelVersions(configs, appliedMigrations) {
24272
24317
  // src/shared/migration/config-migration.ts
24273
24318
  init_logger();
24274
24319
  import * as fs2 from "fs";
24275
- function migrateConfigFile(configPath, rawConfig) {
24320
+ function migrateConfigFile(configPath, rawConfig, options) {
24321
+ const { writeToDisk = true } = options ?? {};
24276
24322
  const copy = structuredClone(rawConfig);
24277
24323
  let needsWrite = false;
24278
24324
  const existingMigrations = Array.isArray(copy._migrations) ? new Set(copy._migrations) : new Set;
@@ -24352,31 +24398,33 @@ function migrateConfigFile(configPath, rawConfig) {
24352
24398
  }
24353
24399
  }
24354
24400
  if (needsWrite) {
24355
- const timestamp2 = new Date().toISOString().replace(/[:.]/g, "-");
24356
- const backupPath = `${configPath}.bak.${timestamp2}`;
24357
- let backupSucceeded = false;
24358
- try {
24359
- fs2.copyFileSync(configPath, backupPath);
24360
- backupSucceeded = true;
24361
- } catch {}
24362
- let writeSucceeded = false;
24363
- try {
24364
- fs2.writeFileSync(configPath, JSON.stringify(copy, null, 2) + `
24365
- `, "utf-8");
24366
- writeSucceeded = true;
24367
- } catch (err) {
24368
- log(`Failed to write migrated config to ${configPath}:`, err);
24369
- }
24370
24401
  for (const key of Object.keys(rawConfig)) {
24371
24402
  delete rawConfig[key];
24372
24403
  }
24373
24404
  Object.assign(rawConfig, copy);
24374
- if (writeSucceeded) {
24375
- const backupMessage = backupSucceeded ? ` (backup: ${backupPath})` : "";
24376
- log(`Migrated config file: ${configPath}${backupMessage}`);
24377
- } else {
24378
- const backupMessage = backupSucceeded ? ` (backup: ${backupPath})` : "";
24379
- log(`Applied migrated config in-memory for: ${configPath}${backupMessage}`);
24405
+ if (writeToDisk) {
24406
+ const timestamp2 = new Date().toISOString().replace(/[:.]/g, "-");
24407
+ const backupPath = `${configPath}.bak.${timestamp2}`;
24408
+ let backupSucceeded = false;
24409
+ try {
24410
+ fs2.copyFileSync(configPath, backupPath);
24411
+ backupSucceeded = true;
24412
+ } catch {}
24413
+ let writeSucceeded = false;
24414
+ try {
24415
+ fs2.writeFileSync(configPath, JSON.stringify(copy, null, 2) + `
24416
+ `, "utf-8");
24417
+ writeSucceeded = true;
24418
+ } catch (err) {
24419
+ log(`Failed to write migrated config to ${configPath}:`, err);
24420
+ }
24421
+ if (writeSucceeded) {
24422
+ const backupMessage = backupSucceeded ? ` (backup: ${backupPath})` : "";
24423
+ log(`Migrated config file: ${configPath}${backupMessage}`);
24424
+ } else {
24425
+ const backupMessage = backupSucceeded ? ` (backup: ${backupPath})` : "";
24426
+ log(`Applied migrated config in-memory for: ${configPath}${backupMessage}`);
24427
+ }
24380
24428
  }
24381
24429
  }
24382
24430
  return needsWrite;
@@ -24854,6 +24902,10 @@ init_logger();
24854
24902
 
24855
24903
  // src/features/claude-code-plugin-loader/hook-loader.ts
24856
24904
  init_logger();
24905
+ // src/shared/index.ts
24906
+ init_install_defaults_contract();
24907
+ init_computed_install_defaults();
24908
+
24857
24909
  // src/shared/session-category-registry.ts
24858
24910
  var sessionCategoryMap = new Map;
24859
24911
  // src/plugin-config.ts
@@ -24903,7 +24955,7 @@ function loadConfigFromPath(configPath, _ctx) {
24903
24955
  if (fs3.existsSync(configPath)) {
24904
24956
  const content = fs3.readFileSync(configPath, "utf-8");
24905
24957
  const rawConfig = parseJsonc(content);
24906
- migrateConfigFile(configPath, rawConfig);
24958
+ migrateConfigFile(configPath, rawConfig, { writeToDisk: false });
24907
24959
  const result = DrizzyAgentConfigSchema.safeParse(rawConfig);
24908
24960
  if (result.success) {
24909
24961
  log(`Config loaded from ${configPath}`, { agents: result.data.agents });
@@ -24933,6 +24985,7 @@ function mergeConfigs(base, override) {
24933
24985
  return {
24934
24986
  ...base,
24935
24987
  ...override,
24988
+ _install_defaults: override._install_defaults ?? base._install_defaults,
24936
24989
  agents: deepMerge(base.agents, override.agents),
24937
24990
  categories: deepMerge(base.categories, override.categories),
24938
24991
  disabled_agents: [
@@ -24968,6 +25021,46 @@ function mergeConfigs(base, override) {
24968
25021
  claude_code: deepMerge(base.claude_code, override.claude_code)
24969
25022
  };
24970
25023
  }
25024
+ function isValidUserSnapshot(snapshot) {
25025
+ if (typeof snapshot !== "object" || snapshot === null) {
25026
+ return false;
25027
+ }
25028
+ const s = snapshot;
25029
+ if (s.snapshot_version !== 1) {
25030
+ return false;
25031
+ }
25032
+ if (typeof s.providers !== "object" || s.providers === null) {
25033
+ return false;
25034
+ }
25035
+ return true;
25036
+ }
25037
+ function stripInvalidUserInstallDefaults(config2, configPath) {
25038
+ if (!config2._install_defaults) {
25039
+ return config2;
25040
+ }
25041
+ if (isValidUserSnapshot(config2._install_defaults)) {
25042
+ return config2;
25043
+ }
25044
+ log(`Ignoring invalid _install_defaults from ${configPath}`);
25045
+ addConfigLoadError({
25046
+ path: configPath,
25047
+ error: "Ignoring invalid _install_defaults section, falling back to built-in defaults"
25048
+ });
25049
+ const { _install_defaults: _ignored, ...explicitConfig } = config2;
25050
+ return explicitConfig;
25051
+ }
25052
+ function stripProjectInstallDefaults(config2, configPath) {
25053
+ if (!config2?._install_defaults) {
25054
+ return config2;
25055
+ }
25056
+ log(`Ignoring project _install_defaults from ${configPath}`);
25057
+ addConfigLoadError({
25058
+ path: configPath,
25059
+ error: "Ignoring project _install_defaults, snapshot defaults are user-config only"
25060
+ });
25061
+ const { _install_defaults: _ignored, ...explicitConfig } = config2;
25062
+ return explicitConfig;
25063
+ }
24971
25064
  function loadPluginConfig(directory, ctx) {
24972
25065
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
24973
25066
  const userBasePath = path3.join(configDir, "drizzy-agent");
@@ -24976,8 +25069,10 @@ function loadPluginConfig(directory, ctx) {
24976
25069
  const projectBasePath = path3.join(directory, ".opencode", "drizzy-agent");
24977
25070
  const projectDetected = detectConfigFile(projectBasePath);
24978
25071
  const projectConfigPath = projectDetected.format !== "none" ? projectDetected.path : projectBasePath + ".json";
24979
- let config2 = loadConfigFromPath(userConfigPath, ctx) ?? {};
24980
- const projectConfig = loadConfigFromPath(projectConfigPath, ctx);
25072
+ const rawUserConfig = loadConfigFromPath(userConfigPath, ctx) ?? {};
25073
+ const userConfig = stripInvalidUserInstallDefaults(rawUserConfig, userConfigPath);
25074
+ let config2 = mergeConfigs(synthesizeComputedDefaultsConfig(userConfig._install_defaults), userConfig);
25075
+ const projectConfig = stripProjectInstallDefaults(loadConfigFromPath(projectConfigPath, ctx), projectConfigPath);
24981
25076
  if (projectConfig) {
24982
25077
  config2 = mergeConfigs(config2, projectConfig);
24983
25078
  }