@ekzs/cli 0.2.0 → 0.3.3

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 (67) hide show
  1. package/README.md +49 -13
  2. package/dist/commands/agent.d.ts +0 -4
  3. package/dist/commands/agent.d.ts.map +1 -1
  4. package/dist/commands/agent.js +0 -6
  5. package/dist/commands/ask.d.ts +3 -5
  6. package/dist/commands/ask.d.ts.map +1 -1
  7. package/dist/commands/ask.js +50 -36
  8. package/dist/commands/local-agent.d.ts +0 -2
  9. package/dist/commands/local-agent.d.ts.map +1 -1
  10. package/dist/commands/local-agent.js +292 -131
  11. package/dist/commands/providers.d.ts +4 -0
  12. package/dist/commands/providers.d.ts.map +1 -0
  13. package/dist/commands/providers.js +6 -0
  14. package/dist/commands/setup.d.ts +5 -0
  15. package/dist/commands/setup.d.ts.map +1 -0
  16. package/dist/commands/setup.js +4 -0
  17. package/dist/index.js +41 -20
  18. package/dist/lib/commands-i18n.d.ts +12 -1
  19. package/dist/lib/commands-i18n.d.ts.map +1 -1
  20. package/dist/lib/commands-i18n.js +78 -14
  21. package/dist/lib/composer-model.d.ts +1 -1
  22. package/dist/lib/composer-model.d.ts.map +1 -1
  23. package/dist/lib/composer-model.js +2 -2
  24. package/dist/lib/global-config.d.ts +19 -0
  25. package/dist/lib/global-config.d.ts.map +1 -0
  26. package/dist/lib/global-config.js +52 -0
  27. package/dist/lib/help.d.ts.map +1 -1
  28. package/dist/lib/help.js +16 -12
  29. package/dist/lib/locale.d.ts.map +1 -1
  30. package/dist/lib/locale.js +2 -1
  31. package/dist/lib/onboarding.d.ts +20 -0
  32. package/dist/lib/onboarding.d.ts.map +1 -0
  33. package/dist/lib/onboarding.js +129 -0
  34. package/dist/lib/providers/agent-runner.d.ts +12 -0
  35. package/dist/lib/providers/agent-runner.d.ts.map +1 -0
  36. package/dist/lib/providers/agent-runner.js +167 -0
  37. package/dist/lib/providers/catalog.d.ts +15 -0
  38. package/dist/lib/providers/catalog.d.ts.map +1 -0
  39. package/dist/lib/providers/catalog.js +93 -0
  40. package/dist/lib/providers/chat.d.ts +10 -0
  41. package/dist/lib/providers/chat.d.ts.map +1 -0
  42. package/dist/lib/providers/chat.js +121 -0
  43. package/dist/lib/providers/credentials.d.ts +28 -0
  44. package/dist/lib/providers/credentials.d.ts.map +1 -0
  45. package/dist/lib/providers/credentials.js +70 -0
  46. package/dist/lib/providers/cursor-runner.d.ts +13 -0
  47. package/dist/lib/providers/cursor-runner.d.ts.map +1 -0
  48. package/dist/lib/providers/cursor-runner.js +89 -0
  49. package/dist/lib/providers/cursor-sdk.d.ts +8 -0
  50. package/dist/lib/providers/cursor-sdk.d.ts.map +1 -0
  51. package/dist/lib/providers/cursor-sdk.js +25 -0
  52. package/dist/lib/providers/store.d.ts +21 -0
  53. package/dist/lib/providers/store.d.ts.map +1 -0
  54. package/dist/lib/providers/store.js +84 -0
  55. package/dist/lib/providers/tools.d.ts +80 -0
  56. package/dist/lib/providers/tools.d.ts.map +1 -0
  57. package/dist/lib/providers/tools.js +145 -0
  58. package/dist/lib/providers/ui.d.ts +4 -0
  59. package/dist/lib/providers/ui.d.ts.map +1 -0
  60. package/dist/lib/providers/ui.js +188 -0
  61. package/dist/lib/setup-messages.d.ts +4 -0
  62. package/dist/lib/setup-messages.d.ts.map +1 -0
  63. package/dist/lib/setup-messages.js +13 -0
  64. package/dist/lib/ui/splash.d.ts.map +1 -1
  65. package/dist/lib/ui/splash.js +4 -0
  66. package/package.json +10 -2
  67. package/skills/ekz-sdk-cli/SKILL.md +6 -4
package/README.md CHANGED
@@ -8,19 +8,56 @@ Runs **in your repo** with Composer 2.5 Fast. Edits files directly.
8
8
 
9
9
  ```bash
10
10
  npm install -g @ekzs/cli
11
- # or from this monorepo:
12
- npm run build --prefix packages/cli
13
11
  ```
14
12
 
15
- ## Setup
13
+ Clean install (~15 packages) — BYOK with OpenAI, Anthropic, DeepSeek, etc. Keys via `ekz providers`.
14
+
15
+ **Cursor provider only** (optional — pulls native deps + npm deprecation noise from `@cursor/sdk`):
16
+
17
+ ```bash
18
+ npm install -g @cursor/sdk
19
+ ```
20
+
21
+ Or install both at once:
16
22
 
17
23
  ```bash
18
- export CURSOR_API_KEY=crsr_... # https://cursor.com/dashboard/integrations
19
- # or add CURSOR_API_KEY to .env.local in your project
20
- export EKZ_AGENT_MODEL=composer-2.5-fast # optional; maps to fast param
24
+ npm install -g @ekzs/cli @cursor/sdk
21
25
  ```
22
26
 
23
- Loads `.env.local` / `.env` from your project automatically.
27
+ ## Setup (BYOK)
28
+
29
+ First run:
30
+
31
+ ```bash
32
+ ekz setup
33
+ # aliases: ekz configurar · ekz 设置
34
+ ```
35
+
36
+ Wizard steps: **language** → **your name** → **Configurar provedor** (or offline).
37
+
38
+ Stored in `~/.ekz/config.json` (name, locale) and `~/.ekz/providers.json` (API keys).
39
+
40
+ Without a provider, `ekz` shows:
41
+
42
+ ```
43
+ Setup required · Configuração necessária · 需要设置
44
+
45
+ pt: Corre `ekz setup` ou `ekz --offline`
46
+ en: Run `ekz setup` or `ekz --offline`
47
+ zh: 运行 `ekz setup` 或 `ekz --offline`
48
+ ```
49
+
50
+ Supported providers: **Cursor**, **OpenAI**, **Anthropic**, **DeepSeek**, **Kimi**, **Groq**, **Mistral**, **Google Gemini**, **Ollama**.
51
+
52
+ | Mode | BYOK behavior |
53
+ |------|----------------|
54
+ | `agent` | Cursor → local SDK; others → tool loop (read/write/run) |
55
+ | `plan` | Same, plan-first instructions |
56
+ | `ask` | Chat only (Cursor uses lightweight Cursor agent) |
57
+
58
+ **Localized commands:** `ekz provedores` / `/provedores` (PT) · `ekz 供应商` / `/供应商` (ZH)
59
+
60
+ Optional: `--api-key crsr_...` for a one-off Cursor override in agent/plan mode.
24
61
 
25
62
  ### Run from this repo
26
63
 
@@ -63,11 +100,12 @@ Prefer built-in CLI helpers over manual curls: `ekz doctor`, `ekz health`, `ekz
63
100
  |---------|-------------|
64
101
  | `ekz` / `ekz agent` | Interactive REPL (default mode: agent) |
65
102
  | `ekz --mode plan` | Plan before editing files |
66
- | `ekz --mode ask` | Remote Q&A, no file edits |
103
+ | `ekz --mode ask` | BYOK Q&A, no file edits |
67
104
  | `ekz agent "fix webhook"` | One-shot task with file edits |
68
105
  | `ekz fix` | Full integration audit + auto-fix pass |
69
- | `ekz ask "…"` | Remote Q&A only (Ekz Connect API) |
106
+ | `ekz ask "…"` | BYOK Q&A only |
70
107
  | `ekz ask -i` | Interactive ask REPL |
108
+ | `ekz providers` | Configure LLM API keys (BYOK) |
71
109
  | `ekz agent --resume` | Continue last session (`.ekz/session.json`) |
72
110
  | `ekz doctor` | Env + OAuth health (free) |
73
111
  | `ekz health` | OAuth + rail health (free) |
@@ -76,7 +114,7 @@ Prefer built-in CLI helpers over manual curls: `ekz doctor`, `ekz health`, `ekz
76
114
 
77
115
  ### REPL shortcuts
78
116
 
79
- Type `/help` in the REPL for the full list (modes, CLI commands, flags). Also: `/mode agent|plan|ask`, `/doctor`, `/scan`, `/lang pt|en|zh`, `/save`, `/resume`.
117
+ Type `/help` in the REPL for the full list (modes, CLI commands, flags). Also: `/providers`, `/mode agent|plan|ask`, `/doctor`, `/scan`, `/lang pt|en|zh`, `/save`, `/resume`.
80
118
 
81
119
  ## Examples
82
120
 
@@ -104,9 +142,7 @@ ekz agent --resume
104
142
 
105
143
  ## Billing
106
144
 
107
- Local agent runs bill through your **Cursor account** (Composer usage). Free commands: `doctor`, `health`, `scan`, `webhook`.
108
-
109
- Remote `ekz ask` uses **EKZ_AGENT_API_KEY** credits on Ekz Connect.
145
+ You bring your own keys (BYOK). Usage bills through **your** provider account (Cursor, OpenAI, Anthropic, etc.). Free commands: `doctor`, `health`, `scan`, `webhook`.
110
146
 
111
147
  ## Session file
112
148
 
@@ -4,8 +4,6 @@ export declare function runAgentCommand(opts: {
4
4
  cwd: string;
5
5
  question?: string;
6
6
  apiKey?: string;
7
- askApiKey?: string;
8
- askApiUrl?: string;
9
7
  askOffline?: boolean;
10
8
  resume?: boolean;
11
9
  fresh?: boolean;
@@ -17,8 +15,6 @@ export declare function runAgentCommand(opts: {
17
15
  export declare function runFixCommand(opts: {
18
16
  cwd: string;
19
17
  apiKey?: string;
20
- askApiKey?: string;
21
- askApiUrl?: string;
22
18
  askOffline?: boolean;
23
19
  resume?: boolean;
24
20
  fresh?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK3C,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,iBAiCA;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,iBAmBA"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK3C,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,iBA6BA;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,iBAiBA"}
@@ -8,8 +8,6 @@ export async function runAgentCommand(opts) {
8
8
  await runInteractiveAgent({
9
9
  cwd: opts.cwd,
10
10
  apiKey: opts.apiKey,
11
- askApiKey: opts.askApiKey,
12
- askApiUrl: opts.askApiUrl,
13
11
  askOffline: opts.askOffline,
14
12
  resume: opts.resume,
15
13
  fresh: opts.fresh,
@@ -23,8 +21,6 @@ export async function runAgentCommand(opts) {
23
21
  cwd: opts.cwd,
24
22
  task: opts.question.trim(),
25
23
  apiKey: opts.apiKey,
26
- askApiKey: opts.askApiKey,
27
- askApiUrl: opts.askApiUrl,
28
24
  askOffline: opts.askOffline,
29
25
  resume: opts.resume,
30
26
  fresh: opts.fresh,
@@ -42,8 +38,6 @@ export async function runFixCommand(opts) {
42
38
  cwd: opts.cwd,
43
39
  task,
44
40
  apiKey: opts.apiKey,
45
- askApiKey: opts.askApiKey,
46
- askApiUrl: opts.askApiUrl,
47
41
  askOffline: opts.askOffline,
48
42
  resume: opts.resume,
49
43
  fresh: opts.fresh,
@@ -1,19 +1,17 @@
1
+ import { type EkzLocale } from "../lib/locale.js";
1
2
  export type AskOptions = {
2
3
  cwd: string;
3
4
  question: string;
4
- apiKey?: string;
5
- apiUrl?: string;
6
5
  offline?: boolean;
7
6
  quiet?: boolean;
7
+ locale?: EkzLocale;
8
8
  };
9
- /** Remote Q&A via Ekz Connect API (no local edits). */
9
+ /** Q&A via BYOK provider (no local file edits). */
10
10
  export declare function executeAskTurn(options: AskOptions): Promise<boolean>;
11
11
  export declare function runAsk(options: Omit<AskOptions, "quiet">): Promise<void>;
12
12
  export declare function runInteractiveAsk(opts: {
13
13
  cwd: string;
14
14
  locale?: string;
15
- apiKey?: string;
16
- apiUrl?: string;
17
15
  offline?: boolean;
18
16
  }): Promise<void>;
19
17
  export declare function resolveQuestion(arg?: string): Promise<string | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAkCA,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,uDAAuD;AACvD,wBAAsB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA2C1E;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,iBAK9D;AAgDD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,iBAuDA;AAED,wBAAsB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM/E"}
1
+ {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAA0B,MAAM,kBAAkB,CAAC;AAyC1E,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,mDAAmD;AACnD,wBAAsB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA0B1E;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,iBAO9D;AAgDD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,iBAwEA;AAED,wBAAsB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM/E"}
@@ -1,14 +1,19 @@
1
1
  import { printHelp, printWelcome } from "../lib/banner.js";
2
- import { resolveReplMetaCommand } from "../lib/commands-i18n.js";
2
+ import { isProvidersMetaCommand, providersCommandLabels, resolveReplMetaCommand } from "../lib/commands-i18n.js";
3
3
  import { buildBootstrapContext } from "../lib/context.js";
4
4
  import { parseLocale, uiStrings } from "../lib/locale.js";
5
5
  import { parseMode } from "../lib/mode.js";
6
6
  import { fail, heading, info, ok } from "../lib/output.js";
7
7
  import { loadPreferences, savePreferences } from "../lib/preferences.js";
8
+ import { ensureAgentReady, offlineModelLabel } from "../lib/onboarding.js";
9
+ import { executeByokAskTurn } from "../lib/providers/chat.js";
10
+ import { executeCursorAskTurn } from "../lib/providers/cursor-runner.js";
11
+ import { isCursorProvider, requireActiveCredentials, ProviderSetupError, } from "../lib/providers/credentials.js";
12
+ import { runProvidersInteractive } from "../lib/providers/ui.js";
8
13
  import { redactText } from "../lib/redact.js";
9
14
  import { askWithPlaceholder, formatInputPrompt } from "../lib/ui/prompt.js";
10
15
  import { inputPlaceholder } from "../lib/ui/splash.js";
11
- function deterministicAnswer(question, ctx) {
16
+ function deterministicAnswer(question, ctx, locale) {
12
17
  const lines = ["## Ekz assistant (offline)", "", question, ""];
13
18
  if (ctx.doctor.issues.length) {
14
19
  lines.push("### Issues", ...ctx.doctor.issues.map((i) => `- ${i}`), "");
@@ -16,48 +21,41 @@ function deterministicAnswer(question, ctx) {
16
21
  if (ctx.scan.length) {
17
22
  lines.push("### Scan", ...ctx.scan.slice(0, 8).map((f) => `- \`${f.file}:${f.line}\` · ${f.message}`), "");
18
23
  }
19
- lines.push("### Next steps", "- Set EKZ_AGENT_API_KEY for remote answers", "- Or run `ekz --mode agent` for local file edits");
24
+ lines.push("### Next steps", "- Run `ekz setup` to configure language, name, and provider", "- Or `ekz --offline` for doctor/scan only");
20
25
  return lines.join("\n");
21
26
  }
22
- /** Remote Q&A via Ekz Connect API (no local edits). */
27
+ /** Q&A via BYOK provider (no local file edits). */
23
28
  export async function executeAskTurn(options) {
24
- const { cwd, question, apiKey, apiUrl, offline, quiet } = options;
25
- const ctx = await buildBootstrapContext(cwd);
26
- const key = apiKey ?? process.env.EKZ_AGENT_API_KEY;
27
- const baseUrl = apiUrl ?? process.env.EKZ_AGENT_API_URL ?? process.env.EKZ_CONNECT_URL ?? "http://localhost:3000";
28
- if (offline || !key) {
29
+ const { cwd, question, offline, quiet, locale = "pt" } = options;
30
+ if (offline) {
31
+ const ctx = await buildBootstrapContext(cwd);
29
32
  if (!quiet)
30
33
  info("Ask mode (offline) · no file edits.\n");
31
- console.log("\n" + deterministicAnswer(question, ctx));
34
+ console.log("\n" + deterministicAnswer(question, ctx, locale));
32
35
  return true;
33
36
  }
34
- const res = await fetch(`${baseUrl.replace(/\/$/, "")}/api/developers/agent`, {
35
- method: "POST",
36
- headers: {
37
- "Content-Type": "application/json",
38
- Authorization: `Bearer ${key}`,
39
- },
40
- body: JSON.stringify({
41
- question,
42
- context: { doctor: ctx.doctor, scan: ctx.scan, env: ctx.env },
43
- }),
44
- });
45
- const data = (await res.json().catch(() => ({})));
46
- if (!res.ok) {
47
- fail(data.error ?? `API error (${res.status})`);
48
- return false;
37
+ let creds;
38
+ try {
39
+ creds = requireActiveCredentials(locale);
40
+ }
41
+ catch (err) {
42
+ if (err instanceof ProviderSetupError) {
43
+ fail(err.message);
44
+ return false;
45
+ }
46
+ throw err;
49
47
  }
50
- if (!quiet)
51
- ok(`${data.mode ?? "online"}${data.model ? ` · ${data.model}` : ""}`);
52
- if (data.creditsRemaining !== undefined)
53
- info(`Credits: ${data.creditsRemaining}`);
54
- console.log("\n" + (data.answer ?? ""));
55
- return true;
48
+ if (isCursorProvider(creds)) {
49
+ return executeCursorAskTurn({ cwd, question, creds, locale });
50
+ }
51
+ return executeByokAskTurn({ cwd, question, locale, creds, quiet });
56
52
  }
57
53
  export async function runAsk(options) {
54
+ const locale = options.locale ?? "pt";
55
+ const p = providersCommandLabels(locale);
58
56
  heading("Ekz ask");
59
- info("Remote Q&A mode · no file edits. Use `ekz --mode agent` for local fixes.\n");
60
- const okResult = await executeAskTurn({ ...options, quiet: false });
57
+ info(`BYOK Q&A · configure keys with \`${p.cli}\` or \`${p.repl}\`.\n`);
58
+ const okResult = await executeAskTurn({ ...options, quiet: false, locale });
61
59
  if (!okResult)
62
60
  process.exitCode = 1;
63
61
  }
@@ -96,10 +94,23 @@ function handleAskMeta(line, locale) {
96
94
  export async function runInteractiveAsk(opts) {
97
95
  const prefs = loadPreferences(opts.cwd, opts.locale, "ask");
98
96
  let currentLocale = prefs.locale;
97
+ let askOffline = Boolean(opts.offline);
98
+ try {
99
+ const ready = await ensureAgentReady({
100
+ locale: currentLocale,
101
+ offlineFlag: opts.offline,
102
+ });
103
+ askOffline = ready.offline || askOffline;
104
+ }
105
+ catch (err) {
106
+ fail(err instanceof Error ? err.message : String(err));
107
+ return;
108
+ }
99
109
  printWelcome({
100
110
  cwd: opts.cwd,
101
111
  locale: currentLocale,
102
112
  mode: "ask",
113
+ model: askOffline ? offlineModelLabel(currentLocale) : undefined,
103
114
  });
104
115
  let turn = 0;
105
116
  while (true) {
@@ -109,6 +120,10 @@ export async function runInteractiveAsk(opts) {
109
120
  })).trim();
110
121
  if (!line)
111
122
  continue;
123
+ if (isProvidersMetaCommand(line.trim().split(/\s+/)[0] ?? "")) {
124
+ await runProvidersInteractive(currentLocale);
125
+ continue;
126
+ }
112
127
  const meta = handleAskMeta(line, currentLocale);
113
128
  if (meta?.kind === "handled")
114
129
  continue;
@@ -132,10 +147,9 @@ export async function runInteractiveAsk(opts) {
132
147
  await executeAskTurn({
133
148
  cwd: opts.cwd,
134
149
  question: line,
135
- apiKey: opts.apiKey,
136
- apiUrl: opts.apiUrl,
137
- offline: opts.offline,
150
+ offline: askOffline,
138
151
  quiet: true,
152
+ locale: currentLocale,
139
153
  });
140
154
  console.log("");
141
155
  turn++;
@@ -3,8 +3,6 @@ export type LocalAgentOptions = {
3
3
  cwd: string;
4
4
  task: string;
5
5
  apiKey?: string;
6
- askApiKey?: string;
7
- askApiUrl?: string;
8
6
  askOffline?: boolean;
9
7
  resume?: boolean;
10
8
  fresh?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"local-agent.d.ts","sourceRoot":"","sources":["../../src/commands/local-agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAAsC,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAalF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAkOF,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,iBAuF1D;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,aAAa,CAAC,iBA4D9F"}
1
+ {"version":3,"file":"local-agent.d.ts","sourceRoot":"","sources":["../../src/commands/local-agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAAsC,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAsBlF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AA2PF,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,iBA0I1D;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,aAAa,CAAC,iBAmG9F"}