amicus 3.1.1 → 3.2.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.
@@ -123,7 +123,60 @@ async function resolveLaunchModel(args) {
123
123
  process.exit(1);
124
124
  }
125
125
 
126
+ /**
127
+ * Existing-user one-time onboarding offer (Part 2, Task 9). Prints a single
128
+ * non-blocking notice line pointing configured-but-not-yet-onboarded users at
129
+ * the per-provider cost-aware default picker (`amicus key <provider>`, Task
130
+ * 6). This is a PRINTED LINE, never an interactive prompt -- it must never
131
+ * wait on stdin.
132
+ *
133
+ * Fires only when ALL of:
134
+ * - the session is interactive (a real TTY, and not --json/--quiet)
135
+ * - the user hasn't already seen this notice (`hasTierOnboarded()` false)
136
+ * - at least one DIRECT provider (openai/anthropic/google/deepseek, via
137
+ * `provider-registry.listDirectProviders`) has a key configured
138
+ * - the user hasn't already used the picker (no vendor-named alias set --
139
+ * no key of `config.aliases` matches a direct-provider id)
140
+ *
141
+ * The flag is only ever set when the notice actually printed. If any gate
142
+ * fails, this is a no-op AND the flag is left unset -- a user who later adds
143
+ * a direct key (or a non-interactive run that becomes interactive) can still
144
+ * see the notice once. Wrapped end-to-end in try/catch: a bug here must never
145
+ * break the command it's attached to.
146
+ * @param {object} [args] parsed CLI args (checked for --json/--quiet to gate interactivity)
147
+ */
148
+ function maybeOfferProviderDefaults(args = {}) {
149
+ try {
150
+ const interactive = !!process.stdin.isTTY && !args.json && !args.quiet;
151
+ if (!interactive) { return; }
152
+
153
+ const { hasTierOnboarded, markTierOnboarded, loadConfig } = require('./config');
154
+ if (hasTierOnboarded()) { return; }
155
+
156
+ const { listDirectProviders } = require('./provider-registry');
157
+ const { readApiKeys } = require('./api-key-store');
158
+ const directProviders = listDirectProviders();
159
+ const apiKeys = readApiKeys();
160
+ const hasDirectKey = directProviders.some((p) => apiKeys[p]);
161
+ if (!hasDirectKey) { return; }
162
+
163
+ const config = loadConfig() || {};
164
+ const aliases = (config.aliases && typeof config.aliases === 'object') ? config.aliases : {};
165
+ const hasVendorAlias = directProviders.some((p) => Object.prototype.hasOwnProperty.call(aliases, p));
166
+ if (hasVendorAlias) { return; }
167
+
168
+ process.stdout.write(
169
+ 'Tip: run `amicus key <provider>` to pick a cost-aware default model per provider ' +
170
+ '(avoids defaulting to the priciest flagship).\n'
171
+ );
172
+ markTierOnboarded();
173
+ } catch (_err) {
174
+ // best-effort: never break the command this is attached to
175
+ }
176
+ }
177
+
126
178
  module.exports = {
127
179
  resolveLaunchModel,
128
180
  deriveAlias,
181
+ maybeOfferProviderDefaults,
129
182
  };