@ekzs/cli 0.3.6 → 0.3.8

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 (37) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -1
  3. package/dist/commands/ask.d.ts.map +1 -1
  4. package/dist/commands/ask.js +12 -5
  5. package/dist/commands/local-agent.d.ts.map +1 -1
  6. package/dist/commands/local-agent.js +47 -3
  7. package/dist/lib/context.d.ts +12 -2
  8. package/dist/lib/context.d.ts.map +1 -1
  9. package/dist/lib/context.js +75 -23
  10. package/dist/lib/doctor-quiet.d.ts +3 -1
  11. package/dist/lib/doctor-quiet.d.ts.map +1 -1
  12. package/dist/lib/doctor-quiet.js +16 -2
  13. package/dist/lib/local-answer.d.ts +4 -0
  14. package/dist/lib/local-answer.d.ts.map +1 -0
  15. package/dist/lib/local-answer.js +26 -0
  16. package/dist/lib/providers/agent-runner.d.ts +1 -0
  17. package/dist/lib/providers/agent-runner.d.ts.map +1 -1
  18. package/dist/lib/providers/agent-runner.js +48 -22
  19. package/dist/lib/providers/catalog.d.ts +8 -0
  20. package/dist/lib/providers/catalog.d.ts.map +1 -1
  21. package/dist/lib/providers/catalog.js +35 -1
  22. package/dist/lib/providers/chat.d.ts.map +1 -1
  23. package/dist/lib/providers/chat.js +46 -31
  24. package/dist/lib/providers/cursor-runner.d.ts.map +1 -1
  25. package/dist/lib/providers/cursor-runner.js +16 -8
  26. package/dist/lib/providers/http.d.ts +6 -0
  27. package/dist/lib/providers/http.d.ts.map +1 -0
  28. package/dist/lib/providers/http.js +35 -0
  29. package/dist/lib/providers/ui.d.ts.map +1 -1
  30. package/dist/lib/providers/ui.js +4 -3
  31. package/dist/lib/skills.d.ts +3 -1
  32. package/dist/lib/skills.d.ts.map +1 -1
  33. package/dist/lib/skills.js +18 -8
  34. package/dist/lib/terminal-answer.d.ts +5 -0
  35. package/dist/lib/terminal-answer.d.ts.map +1 -0
  36. package/dist/lib/terminal-answer.js +55 -0
  37. package/package.json +2 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alberto Moises
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ekzs/cli
2
2
 
3
- **Version:** 0.3.6
3
+ **Version:** 0.3.8
4
4
 
5
5
  **Ekz** is a local coding agent for e-Kwanza Pagamento Integrado v2.4 — like Claude Code, but specialized for `@ekzs/connect`, GPO, EMIS ref, and Ticket integrations.
6
6
 
@@ -1 +1 @@
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
+ {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAA0B,MAAM,kBAAkB,CAAC;AA2C1E,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,CAgC1E;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"}
@@ -6,6 +6,8 @@ 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
8
  import { ensureAgentReady, offlineModelLabel } from "../lib/onboarding.js";
9
+ import { localQuickAnswer } from "../lib/local-answer.js";
10
+ import { formatTerminalAnswer } from "../lib/terminal-answer.js";
9
11
  import { executeByokAskTurn } from "../lib/providers/chat.js";
10
12
  import { executeCursorAskTurn } from "../lib/providers/cursor-runner.js";
11
13
  import { isCursorProvider, requireActiveCredentials, ProviderSetupError, } from "../lib/providers/credentials.js";
@@ -14,24 +16,29 @@ import { redactText } from "../lib/redact.js";
14
16
  import { askWithPlaceholder, formatInputPrompt } from "../lib/ui/prompt.js";
15
17
  import { inputPlaceholder } from "../lib/ui/splash.js";
16
18
  function deterministicAnswer(question, ctx, locale) {
17
- const lines = ["## Ekz assistant (offline)", "", question, ""];
19
+ const lines = ["Ekz assistant (offline):", "", question, ""];
18
20
  if (ctx.doctor.issues.length) {
19
- lines.push("### Issues", ...ctx.doctor.issues.map((i) => `- ${i}`), "");
21
+ lines.push("Issues:", ...ctx.doctor.issues.map((i) => `- ${i}`), "");
20
22
  }
21
23
  if (ctx.scan.length) {
22
- lines.push("### Scan", ...ctx.scan.slice(0, 8).map((f) => `- \`${f.file}:${f.line}\` · ${f.message}`), "");
24
+ lines.push("Scan:", ...ctx.scan.slice(0, 8).map((f) => `- \`${f.file}:${f.line}\` · ${f.message}`), "");
23
25
  }
24
- lines.push("### Next steps", "- Run `ekz setup` to configure language, name, and provider", "- Or `ekz --offline` for doctor/scan only");
26
+ lines.push("Next steps:", "- Run `ekz setup` to configure language, name, and provider", "- Or `ekz --offline` for doctor/scan only");
25
27
  return lines.join("\n");
26
28
  }
27
29
  /** Q&A via BYOK provider (no local file edits). */
28
30
  export async function executeAskTurn(options) {
29
31
  const { cwd, question, offline, quiet, locale = "pt" } = options;
32
+ const quick = localQuickAnswer(question, locale);
33
+ if (quick) {
34
+ console.log("\n" + quick);
35
+ return true;
36
+ }
30
37
  if (offline) {
31
38
  const ctx = await buildBootstrapContext(cwd);
32
39
  if (!quiet)
33
40
  info("Ask mode (offline) · no file edits.\n");
34
- console.log("\n" + deterministicAnswer(question, ctx, locale));
41
+ console.log("\n" + formatTerminalAnswer(deterministicAnswer(question, ctx, locale)));
35
42
  return true;
36
43
  }
37
44
  let creds;
@@ -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;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"}
1
+ {"version":3,"file":"local-agent.d.ts","sourceRoot":"","sources":["../../src/commands/local-agent.ts"],"names":[],"mappings":"AAkBA,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;AAiQF,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,iBA0I1D;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,aAAa,CAAC,iBAmG9F"}
@@ -6,9 +6,10 @@ import { inputPlaceholder, printHelp, printLangSwitch, printModeSwitch, printWel
6
6
  import { loadProjectEnv } from "../lib/env.js";
7
7
  import { ensureAgentReady, offlineAgentHint, offlineModelLabel } from "../lib/onboarding.js";
8
8
  import { resolveReplMetaCommand, isProvidersMetaCommand } from "../lib/commands-i18n.js";
9
- import { buildAgentPrompt, buildBootstrapContext } from "../lib/context.js";
9
+ import { buildAgentPrompt, buildBootstrapContext, invalidateBootstrapContext, isLiteTurn, wantsLocalScan, } from "../lib/context.js";
10
10
  import { resolveComposerModel } from "../lib/composer-model.js";
11
11
  import { parseLocale, uiStrings } from "../lib/locale.js";
12
+ import { localQuickAnswer } from "../lib/local-answer.js";
12
13
  import { modeRequiresCursorAgent, parseMode } from "../lib/mode.js";
13
14
  import { loadPreferences, savePreferences } from "../lib/preferences.js";
14
15
  import { runByokAgentTurn, usesCursorSdk } from "../lib/providers/agent-runner.js";
@@ -47,13 +48,14 @@ function providerModelLabel(creds) {
47
48
  return `${creds.definition.name} · ${creds.model}`;
48
49
  }
49
50
  async function runAgentTurn(options) {
51
+ const lite = options.lite ?? isLiteTurn(options.task);
50
52
  if (usesCursorSdk(options.creds)) {
51
53
  if (!options.cursorAgent) {
52
54
  fail(options.locale === "pt" ? "Sem agente Cursor activo." : "No active Cursor agent.");
53
55
  return { cursorAgent: null, ok: false };
54
56
  }
55
- const ctx = await buildBootstrapContext(options.cwd);
56
- const prompt = buildAgentPrompt(options.task, ctx, options.locale);
57
+ const ctx = await buildBootstrapContext(options.cwd, { includeScan: !lite });
58
+ const prompt = buildAgentPrompt(options.task, ctx, options.locale, { lite });
57
59
  try {
58
60
  await streamRun(options.cursorAgent, prompt, options.locale);
59
61
  console.log("\n");
@@ -73,6 +75,7 @@ async function runAgentTurn(options) {
73
75
  creds: options.creds,
74
76
  locale: options.locale,
75
77
  mode: options.mode,
78
+ lite,
76
79
  });
77
80
  return { cursorAgent: options.cursorAgent, ok: okResult };
78
81
  }
@@ -161,10 +164,12 @@ function handleMetaCommand(line, locale, cwd, currentMode) {
161
164
  return { kind: "mode", mode: next };
162
165
  }
163
166
  if (cmd === "doctor") {
167
+ invalidateBootstrapContext(cwd);
164
168
  void runDoctor(cwd).then(() => console.log(""));
165
169
  return { kind: "handled" };
166
170
  }
167
171
  if (cmd === "scan") {
172
+ invalidateBootstrapContext(cwd);
168
173
  void runScan(cwd).then(() => console.log(""));
169
174
  return { kind: "handled" };
170
175
  }
@@ -489,6 +494,21 @@ async function runRepl(opts) {
489
494
  if (live) {
490
495
  currentOffline = false;
491
496
  currentCreds = live;
497
+ if (currentMode !== "ask" && usesCursorSdk(live)) {
498
+ currentAgent = await recreateAgentForMode(opts.cwd, currentAgent, currentMode, opts.apiKey, currentLocale);
499
+ }
500
+ else if (currentAgent) {
501
+ await disposeAgent(currentAgent);
502
+ currentAgent = null;
503
+ }
504
+ }
505
+ else {
506
+ currentOffline = true;
507
+ currentCreds = null;
508
+ if (currentAgent) {
509
+ await disposeAgent(currentAgent);
510
+ currentAgent = null;
511
+ }
492
512
  }
493
513
  continue;
494
514
  }
@@ -569,7 +589,24 @@ async function runRepl(opts) {
569
589
  }
570
590
  console.log("");
571
591
  process.stdout.write(turnDivider());
592
+ const quick = localQuickAnswer(line, currentLocale);
593
+ if (quick) {
594
+ console.log(quick + "\n");
595
+ turn++;
596
+ continue;
597
+ }
598
+ if (wantsLocalScan(line)) {
599
+ invalidateBootstrapContext(opts.cwd);
600
+ info(currentLocale === "pt" ? "Scan local (sem LLM)..." : "Local scan (no LLM)...");
601
+ await runScan(opts.cwd);
602
+ console.log("\n");
603
+ turn++;
604
+ continue;
605
+ }
572
606
  if (currentMode === "ask") {
607
+ if (!currentOffline && resolveCreds()) {
608
+ info(`${currentCreds.definition.name} · ${currentCreds.model}…`);
609
+ }
573
610
  await executeAskTurn({
574
611
  cwd: opts.cwd,
575
612
  question: line,
@@ -591,8 +628,14 @@ async function runRepl(opts) {
591
628
  fail(currentLocale === "pt" ? "Sem agente Cursor activo." : "No active Cursor agent.");
592
629
  continue;
593
630
  }
631
+ info(currentLocale === "pt"
632
+ ? `${creds.definition.name} · ${creds.model}…`
633
+ : currentLocale === "zh"
634
+ ? `${creds.definition.name} · ${creds.model}…`
635
+ : `${creds.definition.name} · ${creds.model}…`);
594
636
  if (currentAgent)
595
637
  await persistAgent(opts.cwd, currentAgent, creds);
638
+ const lite = isLiteTurn(line);
596
639
  const outcome = await runAgentTurn({
597
640
  cwd: opts.cwd,
598
641
  task: line,
@@ -600,6 +643,7 @@ async function runRepl(opts) {
600
643
  locale: currentLocale,
601
644
  mode: currentMode,
602
645
  cursorAgent: currentAgent,
646
+ lite,
603
647
  });
604
648
  if (outcome.cursorAgent)
605
649
  currentAgent = outcome.cursorAgent;
@@ -6,7 +6,17 @@ export type BootstrapContext = {
6
6
  scan: Awaited<ReturnType<typeof collectScanFindings>>;
7
7
  env: Record<string, string>;
8
8
  };
9
- export declare function buildBootstrapContext(cwd: string): Promise<BootstrapContext>;
10
- export declare function buildAgentPrompt(task: string, ctx: BootstrapContext, locale?: EkzLocale): string;
9
+ export declare function invalidateBootstrapContext(cwd?: string): void;
10
+ /** Integration-heavy tasks need repo scan + full skill bundle. */
11
+ export declare function needsIntegrationContext(task: string): boolean;
12
+ export declare function isLiteTurn(task: string): boolean;
13
+ export declare function wantsLocalScan(line: string): boolean;
14
+ export declare function buildBootstrapContext(cwd: string, options?: {
15
+ refresh?: boolean;
16
+ includeScan?: boolean;
17
+ }): Promise<BootstrapContext>;
18
+ export declare function buildAgentPrompt(task: string, ctx: BootstrapContext, locale?: EkzLocale, options?: {
19
+ lite?: boolean;
20
+ }): string;
11
21
  export declare const FIX_DEFAULT_TASK = "Audit this project's e-Kwanza v2.4 integration end-to-end.\nFix env wiring, payment rails (gpo / emis_ref / ticket), checkout flows, and webhook handlers.\nUse @ekzs/connect. Follow the loaded Ekz skills. Stay within e-Kwanza payment scope only \u2014 read unrelated code for context, do not edit it.\nAfter edits, suggest running `ekz doctor`.";
12
22
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAItD,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,aAAa,CAAC;AAIlE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC;IACxD,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC;IACtD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B,CAAC;AAEF,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAalF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,GAAE,SAAgB,GAAG,MAAM,CAoCtG;AAED,eAAO,MAAM,gBAAgB,6VAGgB,CAAC"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/lib/context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAItD,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,aAAa,CAAC;AAMlE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC;IACxD,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC;IACtD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B,CAAC;AASF,wBAAgB,0BAA0B,CAAC,GAAG,CAAC,EAAE,MAAM,QAGtD;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD,OAAO,CAAC,gBAAgB,CAAC,CA6B3B;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,gBAAgB,EACrB,MAAM,GAAE,SAAgB,EACxB,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,MAAM,CA4CR;AAED,eAAO,MAAM,gBAAgB,6VAGgB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { resolve } from "path";
1
2
  import { collectDoctorResult } from "./doctor-quiet.js";
2
3
  import { collectScanFindings } from "./scan-quiet.js";
3
4
  import { loadProjectEnv } from "./env.js";
@@ -6,23 +7,81 @@ import { formatSkillsForPrompt, selectSkillsForTask } from "./skills.js";
6
7
  import { languageInstruction } from "./locale.js";
7
8
  import { agentScopeBlock } from "./scope.js";
8
9
  import { detectShellEnvironment, shellGuidanceBlock } from "./shell.js";
9
- export async function buildBootstrapContext(cwd) {
10
- const doctor = await collectDoctorResult(cwd);
11
- const scan = await collectScanFindings(cwd);
12
- const { env } = loadProjectEnv(cwd);
10
+ import { isLocalQuickTurn } from "./local-answer.js";
11
+ import { terminalStyleInstruction } from "./terminal-answer.js";
12
+ const bootstrapCache = new Map();
13
+ export function invalidateBootstrapContext(cwd) {
14
+ if (cwd)
15
+ bootstrapCache.delete(resolve(cwd));
16
+ else
17
+ bootstrapCache.clear();
18
+ }
19
+ /** Integration-heavy tasks need repo scan + full skill bundle. */
20
+ export function needsIntegrationContext(task) {
21
+ const t = task.trim();
22
+ if (t.length < 4)
23
+ return false;
24
+ if (isLocalQuickTurn(t)) {
25
+ return false;
26
+ }
27
+ return /ekwanza|webhook|gpo|emis|ticket|checkout|integr|doctor|scan|payment|cobran|multicaixa|fix|\.env|rail|sdk|connect|audit|implement|starter|projeto|project|merchant|cobrança|referência|multicaixa|appypay|oauth|env var/i.test(t);
28
+ }
29
+ export function isLiteTurn(task) {
30
+ return !needsIntegrationContext(task);
31
+ }
32
+ export function wantsLocalScan(line) {
33
+ return /\b(scan|analis[aá]r?)\b/i.test(line) && /\b(run|corre|execut|faz|do|faça)\b/i.test(line);
34
+ }
35
+ export async function buildBootstrapContext(cwd, options) {
36
+ const root = resolve(cwd);
37
+ const includeScan = options?.includeScan !== false;
38
+ if (!options?.refresh && bootstrapCache.has(root)) {
39
+ const cached = bootstrapCache.get(root);
40
+ if (!includeScan || cached.scanComplete)
41
+ return cached.ctx;
42
+ }
43
+ const doctor = await collectDoctorResult(root, { live: false });
44
+ const scan = includeScan
45
+ ? await collectScanFindings(root)
46
+ : bootstrapCache.get(root)?.ctx.scan ?? [];
47
+ const { env } = loadProjectEnv(root);
13
48
  const envSnapshot = {};
14
49
  for (const key of Object.keys(env)) {
15
50
  if (key.startsWith("EKWANZA_") || key.startsWith("EKZ_")) {
16
51
  envSnapshot[key] = env[key];
17
52
  }
18
53
  }
19
- return { doctor, scan, env: redactEnv(envSnapshot) };
54
+ const ctx = {
55
+ doctor,
56
+ scan,
57
+ env: redactEnv(envSnapshot),
58
+ };
59
+ bootstrapCache.set(root, { ctx, scanComplete: includeScan });
60
+ return ctx;
20
61
  }
21
- export function buildAgentPrompt(task, ctx, locale = "pt") {
22
- const scanHints = ctx.scan.map((f) => `${f.rule}: ${f.message}`);
23
- const skills = selectSkillsForTask(task, scanHints);
24
- const skillBlock = formatSkillsForPrompt(skills);
62
+ export function buildAgentPrompt(task, ctx, locale = "pt", options) {
63
+ const lite = options?.lite ?? isLiteTurn(task);
64
+ const scanHints = lite ? [] : ctx.scan.map((f) => `${f.rule}: ${f.message}`);
65
+ const skills = selectSkillsForTask(task, scanHints, { lite });
66
+ const skillBlock = lite
67
+ ? `# Skill: ekz-connect\n\nScoped e-Kwanza v2.4 assistant. Use /analisar or /diagnóstico for free local checks.`
68
+ : formatSkillsForPrompt(skills);
25
69
  const shell = detectShellEnvironment();
70
+ const diagnostics = lite
71
+ ? {
72
+ issues: ctx.doctor.issues.slice(0, 6),
73
+ rails: ctx.doctor.rails,
74
+ envKeys: Object.keys(ctx.env),
75
+ }
76
+ : {
77
+ issues: ctx.doctor.issues,
78
+ warnings: ctx.doctor.warnings,
79
+ rails: ctx.doctor.rails,
80
+ health: ctx.doctor.health,
81
+ scan: ctx.scan.slice(0, 12),
82
+ env: ctx.env,
83
+ shell,
84
+ };
26
85
  return `${skillBlock}
27
86
 
28
87
  ---
@@ -32,20 +91,13 @@ Never commit secrets.
32
91
 
33
92
  ${agentScopeBlock(locale)}
34
93
 
35
- ${shellGuidanceBlock(locale)}
36
-
37
- ${languageInstruction(locale)}
38
-
39
- ## Pre-flight diagnostics (ekz doctor + scan)
40
- ${JSON.stringify({
41
- issues: ctx.doctor.issues,
42
- warnings: ctx.doctor.warnings,
43
- rails: ctx.doctor.rails,
44
- health: ctx.doctor.health,
45
- scan: ctx.scan.slice(0, 12),
46
- env: ctx.env,
47
- shell,
48
- }, null, 2)}
94
+ ${lite ? "" : `${shellGuidanceBlock(locale)}\n\n`}
95
+ ${languageInstruction(locale)}
96
+
97
+ ${terminalStyleInstruction(locale)}
98
+
99
+ ## Pre-flight diagnostics (ekz doctor + scan)
100
+ ${JSON.stringify(diagnostics, null, 2)}
49
101
 
50
102
  ## Task
51
103
  ${task}`;
@@ -7,5 +7,7 @@ export type DoctorResult = {
7
7
  warnings: string[];
8
8
  health: Awaited<ReturnType<EkzConnect["healthCheck"]>> | null;
9
9
  };
10
- export declare function collectDoctorResult(cwd: string): Promise<DoctorResult>;
10
+ export declare function collectDoctorResult(cwd: string, options?: {
11
+ live?: boolean;
12
+ }): Promise<DoctorResult>;
11
13
  //# sourceMappingURL=doctor-quiet.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-quiet.d.ts","sourceRoot":"","sources":["../../src/lib/doctor-quiet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAiB,MAAM,eAAe,CAAC;AAE1D,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC/D,CAAC;AAEF,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmC5E"}
1
+ {"version":3,"file":"doctor-quiet.d.ts","sourceRoot":"","sources":["../../src/lib/doctor-quiet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAiB,MAAM,eAAe,CAAC;AAE1D,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC/D,CAAC;AAEF,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,OAAO,CAAC,YAAY,CAAC,CAkDvB"}
@@ -1,6 +1,7 @@
1
1
  import { EkzConnect, configFromEnv } from "@ekzs/connect";
2
2
  import { auditEnv, loadProjectEnv } from "./env.js";
3
- export async function collectDoctorResult(cwd) {
3
+ export async function collectDoctorResult(cwd, options) {
4
+ const live = options?.live !== false;
4
5
  const { envFiles, env } = loadProjectEnv(cwd);
5
6
  const audit = auditEnv(env);
6
7
  const issues = [];
@@ -18,13 +19,26 @@ export async function collectDoctorResult(cwd) {
18
19
  }
19
20
  let health = null;
20
21
  const cfg = configFromEnv(env);
21
- if (cfg) {
22
+ if (cfg && live) {
22
23
  health = await new EkzConnect(cfg).healthCheck();
23
24
  if (!health.oauth)
24
25
  issues.push("OAuth authentication failed");
25
26
  if (!health.configured)
26
27
  warnings.push("healthCheck.configured is false");
27
28
  }
29
+ else if (cfg && !live) {
30
+ health = {
31
+ oauth: false,
32
+ gpo: Boolean(cfg.paymentMethodIdGpo),
33
+ emisRef: Boolean(cfg.paymentMethodIdEmisRef),
34
+ ticket: Boolean(cfg.notificationToken && cfg.apiKey && cfg.partnerRegistrationNumber),
35
+ configured: Boolean(cfg.clientId &&
36
+ cfg.clientSecret &&
37
+ cfg.merchantIdentifier &&
38
+ cfg.apiKey),
39
+ env: cfg.env ?? "sandbox",
40
+ };
41
+ }
28
42
  else {
29
43
  issues.push("Core EKWANZA_* vars missing");
30
44
  }
@@ -0,0 +1,4 @@
1
+ import type { EkzLocale } from "./locale.js";
2
+ export declare function isLocalQuickTurn(input: string): boolean;
3
+ export declare function localQuickAnswer(input: string, locale: EkzLocale): string | null;
4
+ //# sourceMappingURL=local-answer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-answer.d.ts","sourceRoot":"","sources":["../../src/lib/local-answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAO7C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAoBhF"}
@@ -0,0 +1,26 @@
1
+ const GREETING_RE = /^(olá|ola|oi|hello|hi|hey|bom dia|boa tarde|boa noite|你好|您好)[!.?\s]*$/i;
2
+ const THANKS_RE = /^(obrigado|obrigada|thanks|thank you|valeu|谢谢|多谢)[!.?\s]*$/i;
3
+ export function isLocalQuickTurn(input) {
4
+ const text = input.trim();
5
+ return GREETING_RE.test(text) || THANKS_RE.test(text);
6
+ }
7
+ export function localQuickAnswer(input, locale) {
8
+ const text = input.trim();
9
+ if (GREETING_RE.test(text)) {
10
+ if (locale === "zh") {
11
+ return "你好。我可以帮你处理 e-Kwanza 支付集成、checkout、webhook、订阅、票务和用量计费。告诉我你要接入什么,或运行 /scan 做本地诊断。";
12
+ }
13
+ if (locale === "en") {
14
+ return "Hello. I can help with e-Kwanza payments, checkout, webhooks, subscriptions, tickets, and overage billing. Tell me what you want to integrate, or run /scan for a local diagnostic.";
15
+ }
16
+ return "Olá. Posso ajudar com pagamentos e-Kwanza, checkout, webhooks, subscrições, tickets e cobrança de excedentes. Diz-me o que queres integrar, ou corre /scan para um diagnóstico local.";
17
+ }
18
+ if (THANKS_RE.test(text)) {
19
+ if (locale === "zh")
20
+ return "不客气。需要时我可以继续检查支付集成。";
21
+ if (locale === "en")
22
+ return "You are welcome. I can keep checking the payment integration whenever you are ready.";
23
+ return "De nada. Quando quiseres, posso continuar a verificar a integração de pagamentos.";
24
+ }
25
+ return null;
26
+ }
@@ -7,6 +7,7 @@ export declare function runByokAgentTurn(options: {
7
7
  creds: ResolvedCredentials;
8
8
  locale: EkzLocale;
9
9
  mode: EkzMode;
10
+ lite?: boolean;
10
11
  }): Promise<boolean>;
11
12
  export declare function usesCursorSdk(creds: ResolvedCredentials): boolean;
12
13
  //# sourceMappingURL=agent-runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/agent-runner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AA+L5D,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,OAAO,CAAC,CAgCnB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAEjE"}
1
+ {"version":3,"file":"agent-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/agent-runner.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAsM5D,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,OAAO,CAAC,CA0DnB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAEjE"}
@@ -1,34 +1,40 @@
1
1
  import { resolve } from "path";
2
- import { buildAgentPrompt, buildBootstrapContext } from "../context.js";
3
- import { fail } from "../output.js";
2
+ import { buildAgentPrompt, buildBootstrapContext, isLiteTurn, needsIntegrationContext } from "../context.js";
3
+ import { localQuickAnswer } from "../local-answer.js";
4
+ import { fail, warn } from "../output.js";
5
+ import { formatTerminalAnswer } from "../terminal-answer.js";
4
6
  import { toolDone, toolError, toolRunning } from "../theme.js";
7
+ import { deepseekRequestBody, fetchWithTimeout, resolveChatCompletionsUrl } from "./http.js";
5
8
  import { AGENT_TOOL_DEFINITIONS, executeAgentTool } from "./tools.js";
6
- const MAX_TOOL_ROUNDS = 24;
9
+ const MAX_TOOL_ROUNDS = 12;
7
10
  function planInstruction(mode) {
8
11
  if (mode === "plan") {
9
- return "You are in **plan mode**: output a clear numbered plan first. Only call write_file after the plan is clear.";
12
+ return "You are in plan mode: output a clear numbered plan first. Only call write_file after the plan is clear.";
10
13
  }
11
14
  if (mode === "ask") {
12
- return "You are in **ask mode**: answer only. Do not call write_file or run_command.";
15
+ return "You are in ask mode: answer only. Do not call write_file or run_command.";
13
16
  }
14
- return "You are in **agent mode**: use tools to inspect and fix the e-Kwanza integration.";
17
+ return "You are in agent mode: use tools to inspect and fix the e-Kwanza integration.";
15
18
  }
16
- async function openAiAgentLoop(creds, cwd, messages, enableTools) {
17
- const base = (creds.baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
18
- const url = base.endsWith("/chat/completions") ? base : `${base}/chat/completions`;
19
+ async function openAiAgentLoop(creds, cwd, messages, enableTools, thinking = true, timeoutMs) {
20
+ const url = resolveChatCompletionsUrl(creds.baseUrl);
19
21
  for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
20
22
  const headers = { "Content-Type": "application/json" };
21
23
  if (creds.apiKey && creds.apiKey !== "ollama") {
22
24
  headers.Authorization = `Bearer ${creds.apiKey}`;
23
25
  }
24
- const body = {
26
+ const payload = {
25
27
  model: creds.model,
26
28
  messages,
27
29
  temperature: 0.2,
28
30
  };
29
31
  if (enableTools)
30
- body.tools = AGENT_TOOL_DEFINITIONS;
31
- const res = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) });
32
+ payload.tools = AGENT_TOOL_DEFINITIONS;
33
+ const res = await fetchWithTimeout(url, {
34
+ method: "POST",
35
+ headers,
36
+ body: JSON.stringify(deepseekRequestBody(creds.model, payload, { thinking })),
37
+ }, timeoutMs);
32
38
  const data = (await res.json().catch(() => ({})));
33
39
  if (!res.ok)
34
40
  throw new Error(data.error?.message ?? `Provider error (${res.status})`);
@@ -73,7 +79,7 @@ async function openAiAgentLoop(creds, cwd, messages, enableTools) {
73
79
  }
74
80
  throw new Error("Tool loop exceeded maximum rounds.");
75
81
  }
76
- async function anthropicAgentLoop(creds, cwd, system, messages, enableTools) {
82
+ async function anthropicAgentLoop(creds, cwd, system, messages, enableTools, timeoutMs) {
77
83
  const tools = enableTools
78
84
  ? AGENT_TOOL_DEFINITIONS.map((t) => ({
79
85
  name: t.function.name,
@@ -82,7 +88,7 @@ async function anthropicAgentLoop(creds, cwd, system, messages, enableTools) {
82
88
  }))
83
89
  : undefined;
84
90
  for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
85
- const res = await fetch("https://api.anthropic.com/v1/messages", {
91
+ const res = await fetchWithTimeout("https://api.anthropic.com/v1/messages", {
86
92
  method: "POST",
87
93
  headers: {
88
94
  "Content-Type": "application/json",
@@ -96,7 +102,7 @@ async function anthropicAgentLoop(creds, cwd, system, messages, enableTools) {
96
102
  messages,
97
103
  ...(tools ? { tools } : {}),
98
104
  }),
99
- });
105
+ }, timeoutMs);
100
106
  const data = (await res.json().catch(() => ({})));
101
107
  if (!res.ok)
102
108
  throw new Error(data.error?.message ?? `Anthropic error (${res.status})`);
@@ -138,23 +144,43 @@ async function anthropicAgentLoop(creds, cwd, system, messages, enableTools) {
138
144
  }
139
145
  export async function runByokAgentTurn(options) {
140
146
  const cwd = resolve(options.cwd);
141
- const ctx = await buildBootstrapContext(cwd);
142
- const prompt = buildAgentPrompt(options.task, ctx, options.locale);
147
+ const quick = localQuickAnswer(options.task, options.locale);
148
+ if (quick) {
149
+ console.log("\n" + quick);
150
+ return true;
151
+ }
152
+ const lite = options.lite ?? isLiteTurn(options.task);
153
+ const ctx = await buildBootstrapContext(cwd, { includeScan: !lite });
154
+ const prompt = buildAgentPrompt(options.task, ctx, options.locale, { lite });
143
155
  const system = `${planInstruction(options.mode)}\n\n${prompt.split("## Task")[0] ?? prompt}`;
144
156
  const userTask = `## Task\n${options.task}`;
145
- const enableTools = options.mode !== "ask";
157
+ const wantsTools = options.mode !== "ask" && !lite;
158
+ const enableTools = wantsTools && options.creds.definition.capabilities.supportsTools;
159
+ const thinking = !lite &&
160
+ options.creds.definition.capabilities.supportsReasoning &&
161
+ needsIntegrationContext(options.task);
162
+ const timeoutMs = lite
163
+ ? options.creds.definition.capabilities.liteTimeoutMs
164
+ : options.creds.definition.capabilities.agentTimeoutMs;
165
+ if (wantsTools && !enableTools) {
166
+ warn(options.locale === "pt"
167
+ ? `${options.creds.definition.name} não anuncia ferramentas de edição; vou responder sem alterar ficheiros.`
168
+ : options.locale === "zh"
169
+ ? `${options.creds.definition.name} 未声明编辑工具支持;我将只回答,不修改文件。`
170
+ : `${options.creds.definition.name} does not advertise edit tools; answering without changing files.`);
171
+ }
146
172
  try {
147
173
  if (options.creds.definition.kind === "anthropic") {
148
- const text = await anthropicAgentLoop(options.creds, cwd, system, [{ role: "user", content: userTask }], enableTools);
149
- console.log("\n" + text);
174
+ const text = await anthropicAgentLoop(options.creds, cwd, system, [{ role: "user", content: userTask }], enableTools, timeoutMs);
175
+ console.log("\n" + formatTerminalAnswer(text));
150
176
  return true;
151
177
  }
152
178
  const messages = [
153
179
  { role: "system", content: system },
154
180
  { role: "user", content: userTask },
155
181
  ];
156
- const text = await openAiAgentLoop(options.creds, cwd, messages, enableTools);
157
- console.log("\n" + text);
182
+ const text = await openAiAgentLoop(options.creds, cwd, messages, enableTools, thinking, timeoutMs);
183
+ console.log("\n" + formatTerminalAnswer(text));
158
184
  return true;
159
185
  }
160
186
  catch (err) {
@@ -13,6 +13,14 @@ export type ProviderDefinition = {
13
13
  models: string[];
14
14
  keyHint: string;
15
15
  docsUrl: string;
16
+ capabilities: {
17
+ requiresApiKey: boolean;
18
+ supportsTools: boolean;
19
+ supportsStreaming: boolean;
20
+ supportsReasoning: boolean;
21
+ liteTimeoutMs: number;
22
+ agentTimeoutMs: number;
23
+ };
16
24
  };
17
25
  /**
18
26
  * Model IDs synced from official provider docs (May 2026).
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,mBAAmB,GAAG,WAAW,CAAC;AAExE,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,UAAU,GACV,MAAM,GACN,MAAM,GACN,SAAS,GACT,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,kBAAkB,EAiKhD,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAEhF;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,kBAAkB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1F"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,mBAAmB,GAAG,WAAW,CAAC;AAExE,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,UAAU,GACV,MAAM,GACN,MAAM,GACN,SAAS,GACT,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE;QACZ,cAAc,EAAE,OAAO,CAAC;QACxB,aAAa,EAAE,OAAO,CAAC;QACvB,iBAAiB,EAAE,OAAO,CAAC;QAC3B,iBAAiB,EAAE,OAAO,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;CACH,CAAC;AAWF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,kBAAkB,EA2LhD,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAEhF;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,kBAAkB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1F"}
@@ -1,3 +1,11 @@
1
+ const CLOUD_AGENT_CAPABILITIES = {
2
+ requiresApiKey: true,
3
+ supportsTools: true,
4
+ supportsStreaming: false,
5
+ supportsReasoning: false,
6
+ liteTimeoutMs: 20_000,
7
+ agentTimeoutMs: 60_000,
8
+ };
1
9
  /**
2
10
  * Model IDs synced from official provider docs (May 2026).
3
11
  * Sources: cursor.com/docs, developers.openai.com, platform.claude.com,
@@ -24,6 +32,14 @@ export const PROVIDER_CATALOG = [
24
32
  ],
25
33
  keyHint: "crsr_…",
26
34
  docsUrl: "https://cursor.com/dashboard/integrations",
35
+ capabilities: {
36
+ requiresApiKey: true,
37
+ supportsTools: true,
38
+ supportsStreaming: true,
39
+ supportsReasoning: false,
40
+ liteTimeoutMs: 20_000,
41
+ agentTimeoutMs: 90_000,
42
+ },
27
43
  },
28
44
  {
29
45
  id: "openai",
@@ -46,6 +62,7 @@ export const PROVIDER_CATALOG = [
46
62
  ],
47
63
  keyHint: "sk-…",
48
64
  docsUrl: "https://platform.openai.com/api-keys",
65
+ capabilities: CLOUD_AGENT_CAPABILITIES,
49
66
  },
50
67
  {
51
68
  id: "anthropic",
@@ -63,6 +80,7 @@ export const PROVIDER_CATALOG = [
63
80
  ],
64
81
  keyHint: "sk-ant-…",
65
82
  docsUrl: "https://console.anthropic.com/settings/keys",
83
+ capabilities: CLOUD_AGENT_CAPABILITIES,
66
84
  },
67
85
  {
68
86
  id: "deepseek",
@@ -70,10 +88,14 @@ export const PROVIDER_CATALOG = [
70
88
  kind: "openai-compatible",
71
89
  description: "DeepSeek V4 Pro · V4 Flash",
72
90
  defaultModel: "deepseek-v4-flash",
73
- defaultBaseUrl: "https://api.deepseek.com",
91
+ defaultBaseUrl: "https://api.deepseek.com/v1",
74
92
  models: ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"],
75
93
  keyHint: "sk-…",
76
94
  docsUrl: "https://platform.deepseek.com/api_keys",
95
+ capabilities: {
96
+ ...CLOUD_AGENT_CAPABILITIES,
97
+ supportsReasoning: true,
98
+ },
77
99
  },
78
100
  {
79
101
  id: "kimi",
@@ -91,6 +113,7 @@ export const PROVIDER_CATALOG = [
91
113
  ],
92
114
  keyHint: "sk-…",
93
115
  docsUrl: "https://platform.kimi.ai/docs/models",
116
+ capabilities: CLOUD_AGENT_CAPABILITIES,
94
117
  },
95
118
  {
96
119
  id: "groq",
@@ -109,6 +132,7 @@ export const PROVIDER_CATALOG = [
109
132
  ],
110
133
  keyHint: "gsk_…",
111
134
  docsUrl: "https://console.groq.com/docs/models",
135
+ capabilities: CLOUD_AGENT_CAPABILITIES,
112
136
  },
113
137
  {
114
138
  id: "mistral",
@@ -127,6 +151,7 @@ export const PROVIDER_CATALOG = [
127
151
  ],
128
152
  keyHint: "…",
129
153
  docsUrl: "https://docs.mistral.ai/models/overview",
154
+ capabilities: CLOUD_AGENT_CAPABILITIES,
130
155
  },
131
156
  {
132
157
  id: "google",
@@ -145,6 +170,7 @@ export const PROVIDER_CATALOG = [
145
170
  ],
146
171
  keyHint: "AI…",
147
172
  docsUrl: "https://ai.google.dev/gemini-api/docs/models",
173
+ capabilities: CLOUD_AGENT_CAPABILITIES,
148
174
  },
149
175
  {
150
176
  id: "ollama",
@@ -164,6 +190,14 @@ export const PROVIDER_CATALOG = [
164
190
  ],
165
191
  keyHint: "ollama (optional)",
166
192
  docsUrl: "https://ollama.com/library",
193
+ capabilities: {
194
+ requiresApiKey: false,
195
+ supportsTools: false,
196
+ supportsStreaming: false,
197
+ supportsReasoning: false,
198
+ liteTimeoutMs: 10_000,
199
+ agentTimeoutMs: 30_000,
200
+ },
167
201
  },
168
202
  ];
169
203
  export function getProviderDefinition(id) {
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/chat.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGnE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAuG1B,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,OAAO,CAAC,CAgDnB"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/chat.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGnE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAC;AAsH1B,wBAAsB,kBAAkB,CAAC,OAAO,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,OAAO,CAAC,CA6DnB"}
@@ -1,44 +1,51 @@
1
- import { buildBootstrapContext } from "../context.js";
1
+ import { buildBootstrapContext, isLiteTurn, needsIntegrationContext } from "../context.js";
2
2
  import { languageInstruction } from "../locale.js";
3
3
  import { agentScopeBlock } from "../scope.js";
4
4
  import { fail, info, ok } from "../output.js";
5
5
  import { ProviderSetupError, requireActiveCredentials, } from "./credentials.js";
6
- function buildAskSystemPrompt(ctx, locale) {
6
+ import { deepseekRequestBody, fetchWithTimeout, resolveChatCompletionsUrl, } from "./http.js";
7
+ import { formatTerminalAnswer, terminalStyleInstruction } from "../terminal-answer.js";
8
+ function buildAskSystemPrompt(ctx, locale, lite) {
9
+ const diagnostics = lite
10
+ ? { issues: ctx.doctor.issues.slice(0, 6), rails: ctx.doctor.rails, envKeys: Object.keys(ctx.env) }
11
+ : {
12
+ issues: ctx.doctor.issues,
13
+ warnings: ctx.doctor.warnings,
14
+ rails: ctx.doctor.rails,
15
+ health: ctx.doctor.health,
16
+ scan: ctx.scan.slice(0, 12),
17
+ env: ctx.env,
18
+ };
7
19
  return `You are **Ekz Connect** — an e-Kwanza v2.4 integration assistant.
8
20
  Answer in clear, actionable steps. You are in **ask mode**: explain and guide only; do not claim you edited files.
9
21
 
10
22
  ${agentScopeBlock(locale)}
11
23
 
12
- ${languageInstruction(locale)}
13
-
14
- ## Project context (doctor + scan)
15
- ${JSON.stringify({
16
- issues: ctx.doctor.issues,
17
- warnings: ctx.doctor.warnings,
18
- rails: ctx.doctor.rails,
19
- health: ctx.doctor.health,
20
- scan: ctx.scan.slice(0, 12),
21
- env: ctx.env,
22
- }, null, 2)}`;
24
+ ${languageInstruction(locale)}
25
+
26
+ ${terminalStyleInstruction(locale)}
27
+
28
+ ## Project context (doctor + scan)
29
+ ${JSON.stringify(diagnostics, null, 2)}`;
23
30
  }
24
- async function chatOpenAiCompatible(creds, messages) {
25
- const base = (creds.baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
26
- const url = base.endsWith("/chat/completions") ? base : `${base}/chat/completions`;
31
+ async function chatOpenAiCompatible(creds, messages, options) {
32
+ const url = resolveChatCompletionsUrl(creds.baseUrl);
27
33
  const headers = {
28
34
  "Content-Type": "application/json",
29
35
  };
30
36
  if (creds.apiKey && creds.apiKey !== "ollama") {
31
37
  headers.Authorization = `Bearer ${creds.apiKey}`;
32
38
  }
33
- const res = await fetch(url, {
39
+ const body = deepseekRequestBody(creds.model, {
40
+ model: creds.model,
41
+ messages,
42
+ temperature: 0.2,
43
+ }, { thinking: options?.thinking });
44
+ const res = await fetchWithTimeout(url, {
34
45
  method: "POST",
35
46
  headers,
36
- body: JSON.stringify({
37
- model: creds.model,
38
- messages,
39
- temperature: 0.2,
40
- }),
41
- });
47
+ body: JSON.stringify(body),
48
+ }, options?.timeoutMs);
42
49
  const data = (await res.json().catch(() => ({})));
43
50
  if (!res.ok) {
44
51
  throw new Error(data.error?.message ?? `Provider error (${res.status})`);
@@ -48,8 +55,8 @@ async function chatOpenAiCompatible(creds, messages) {
48
55
  throw new Error("Empty response from provider.");
49
56
  return text;
50
57
  }
51
- async function chatAnthropic(creds, system, question) {
52
- const res = await fetch("https://api.anthropic.com/v1/messages", {
58
+ async function chatAnthropic(creds, system, question, timeoutMs) {
59
+ const res = await fetchWithTimeout("https://api.anthropic.com/v1/messages", {
53
60
  method: "POST",
54
61
  headers: {
55
62
  "Content-Type": "application/json",
@@ -62,7 +69,7 @@ async function chatAnthropic(creds, system, question) {
62
69
  system,
63
70
  messages: [{ role: "user", content: question }],
64
71
  }),
65
- });
72
+ }, timeoutMs);
66
73
  const data = (await res.json().catch(() => ({})));
67
74
  if (!res.ok) {
68
75
  throw new Error(data.error?.message ?? `Anthropic error (${res.status})`);
@@ -78,7 +85,8 @@ async function chatAnthropic(creds, system, question) {
78
85
  }
79
86
  export async function executeByokAskTurn(options) {
80
87
  const locale = options.locale ?? "pt";
81
- const ctx = await buildBootstrapContext(options.cwd);
88
+ const lite = isLiteTurn(options.question);
89
+ const ctx = await buildBootstrapContext(options.cwd, { includeScan: !lite });
82
90
  let creds;
83
91
  try {
84
92
  creds = options.creds ?? requireActiveCredentials(locale);
@@ -98,20 +106,27 @@ export async function executeByokAskTurn(options) {
98
106
  : "Cursor ask uses the Cursor agent path — switch provider or use agent mode.");
99
107
  return false;
100
108
  }
101
- const system = buildAskSystemPrompt(ctx, locale);
109
+ const system = buildAskSystemPrompt(ctx, locale, lite);
110
+ const timeoutMs = lite
111
+ ? creds.definition.capabilities.liteTimeoutMs
112
+ : creds.definition.capabilities.agentTimeoutMs;
102
113
  if (!options.quiet) {
103
114
  info(`${creds.definition.name} · ${creds.model}`);
104
115
  }
105
116
  try {
106
117
  const answer = creds.definition.kind === "anthropic"
107
- ? await chatAnthropic(creds, system, options.question)
118
+ ? await chatAnthropic(creds, system, options.question, timeoutMs)
108
119
  : await chatOpenAiCompatible(creds, [
109
120
  { role: "system", content: system },
110
121
  { role: "user", content: options.question },
111
- ]);
122
+ ], {
123
+ thinking: creds.definition.capabilities.supportsReasoning &&
124
+ needsIntegrationContext(options.question),
125
+ timeoutMs,
126
+ });
112
127
  if (!options.quiet)
113
128
  ok(creds.definition.name);
114
- console.log("\n" + answer);
129
+ console.log("\n" + formatTerminalAnswer(answer));
115
130
  return true;
116
131
  }
117
132
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"cursor-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/cursor-runner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,cAAc,CAAC;AAInE,OAAO,EAAiB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,iBAAe,YAAY,CAAC,KAAK,EAAE,QAAQ,iBAI1C;AAED,iBAAe,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC1F;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC;CACnB,GAAG,OAAO,CAAC,OAAO,CAAC,CAmCnB;AAED,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"cursor-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/cursor-runner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,cAAc,CAAC;AAKnE,OAAO,EAAiB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,iBAAe,YAAY,CAAC,KAAK,EAAE,QAAQ,iBAI1C;AAED,iBAAe,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC1F;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC;CACnB,GAAG,OAAO,CAAC,OAAO,CAAC,CA0CnB;AAED,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import { resolve } from "path";
2
- import { buildBootstrapContext } from "../context.js";
2
+ import { buildBootstrapContext, isLiteTurn } from "../context.js";
3
3
  import { resolveComposerModel } from "../composer-model.js";
4
4
  import { languageInstruction } from "../locale.js";
5
5
  import { fail } from "../output.js";
6
6
  import { agentScopeBlock } from "../scope.js";
7
+ import { formatTerminalDelta, terminalStyleInstruction } from "../terminal-answer.js";
7
8
  import { toolDone, toolError, toolRunning } from "../theme.js";
8
9
  import { loadCursorSdk } from "./cursor-sdk.js";
9
10
  async function disposeAgent(agent) {
@@ -17,8 +18,9 @@ async function streamRun(agent, prompt, locale) {
17
18
  const { AgentBusyError, CursorAgentError } = await loadCursorSdk(locale);
18
19
  const sendOptions = {
19
20
  onDelta: ({ update }) => {
20
- if (update.type === "text-delta" && update.text)
21
- process.stdout.write(update.text);
21
+ if (update.type === "text-delta" && update.text) {
22
+ process.stdout.write(formatTerminalDelta(update.text));
23
+ }
22
24
  },
23
25
  };
24
26
  let run;
@@ -51,17 +53,23 @@ async function streamRun(agent, prompt, locale) {
51
53
  }
52
54
  export async function executeCursorAskTurn(options) {
53
55
  const cwd = resolve(options.cwd);
54
- const ctx = await buildBootstrapContext(cwd);
56
+ const lite = isLiteTurn(options.question);
57
+ const ctx = await buildBootstrapContext(cwd, { includeScan: !lite });
55
58
  const model = resolveComposerModel(process.env, options.creds.model);
56
59
  const { Agent } = await loadCursorSdk(options.locale);
60
+ const diagnostics = lite
61
+ ? { issues: ctx.doctor.issues.slice(0, 6), rails: ctx.doctor.rails, envKeys: Object.keys(ctx.env) }
62
+ : { issues: ctx.doctor.issues, scan: ctx.scan.slice(0, 12), env: ctx.env };
57
63
  const prompt = `${agentScopeBlock(options.locale)}
58
64
 
59
- ${languageInstruction(options.locale)}
60
-
61
- You are in **ask mode** — answer only. Do not edit files or run tools unless the user explicitly asks for a command example.
65
+ ${languageInstruction(options.locale)}
66
+
67
+ ${terminalStyleInstruction(options.locale)}
68
+
69
+ You are in ask mode: answer only. Do not edit files or run tools unless the user explicitly asks for a command example.
62
70
 
63
71
  ## Project context
64
- ${JSON.stringify({ issues: ctx.doctor.issues, scan: ctx.scan.slice(0, 12), env: ctx.env }, null, 2)}
72
+ ${JSON.stringify(diagnostics, null, 2)}
65
73
 
66
74
  ## Question
67
75
  ${options.question}`;
@@ -0,0 +1,6 @@
1
+ export declare function resolveChatCompletionsUrl(baseUrl?: string): string;
2
+ export declare function fetchWithTimeout(url: string, init: RequestInit, timeoutMs?: number): Promise<Response>;
3
+ export declare function deepseekRequestBody(model: string, body: Record<string, unknown>, options?: {
4
+ thinking?: boolean;
5
+ }): Record<string, unknown>;
6
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/http.ts"],"names":[],"mappings":"AAEA,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAKlE;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,SAAS,SAAqB,GAC7B,OAAO,CAAC,QAAQ,CAAC,CAanB;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB"}
@@ -0,0 +1,35 @@
1
+ const DEFAULT_TIMEOUT_MS = 60_000;
2
+ export function resolveChatCompletionsUrl(baseUrl) {
3
+ const base = (baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
4
+ if (base.endsWith("/chat/completions"))
5
+ return base;
6
+ if (base.endsWith("/v1"))
7
+ return `${base}/chat/completions`;
8
+ return `${base}/chat/completions`;
9
+ }
10
+ export async function fetchWithTimeout(url, init, timeoutMs = DEFAULT_TIMEOUT_MS) {
11
+ const controller = new AbortController();
12
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
13
+ try {
14
+ return await fetch(url, { ...init, signal: controller.signal });
15
+ }
16
+ catch (err) {
17
+ if (err instanceof Error && err.name === "AbortError") {
18
+ throw new Error(`Provider request timed out after ${Math.round(timeoutMs / 1000)}s.`);
19
+ }
20
+ throw err;
21
+ }
22
+ finally {
23
+ clearTimeout(timer);
24
+ }
25
+ }
26
+ export function deepseekRequestBody(model, body, options) {
27
+ if (!model.startsWith("deepseek-v4"))
28
+ return body;
29
+ if (options?.thinking === false)
30
+ return body;
31
+ return {
32
+ ...body,
33
+ thinking: { type: "enabled" },
34
+ };
35
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/ui.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AA2N9C,gEAAgE;AAChE,wBAAsB,sBAAsB,CAAC,MAAM,GAAE,SAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBvF;AAED,wBAAsB,uBAAuB,CAAC,MAAM,GAAE,SAAgB,iBAuDrE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAMnE"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/ui.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AA4N9C,gEAAgE;AAChE,wBAAsB,sBAAsB,CAAC,MAAM,GAAE,SAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBvF;AAED,wBAAsB,uBAAuB,CAAC,MAAM,GAAE,SAAgB,iBAuDrE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAMnE"}
@@ -140,15 +140,16 @@ async function configureProvider(id, locale) {
140
140
  }
141
141
  const keyPrompt = t(locale, `Cola a API key (${def.keyHint}): `, `Paste API key (${def.keyHint}): `, `粘贴 API key (${def.keyHint}): `);
142
142
  const apiKey = await askLine(keyPrompt, true);
143
- if (!apiKey) {
143
+ if (!apiKey && def.capabilities.requiresApiKey) {
144
144
  warn(t(locale, "Chave vazia — cancelado.", "Empty key — cancelled.", "密钥为空,已取消。"));
145
145
  return false;
146
146
  }
147
+ const storedApiKey = apiKey || def.id;
147
148
  const model = await pickModel(id, locale);
148
149
  if (!model)
149
150
  return false;
150
- setStoredProvider(id, { apiKey, model });
151
- ok(t(locale, `Guardado ${def.name} · ${model} (${maskApiKey(apiKey)})`, `Saved ${def.name} · ${model} (${maskApiKey(apiKey)})`, `已保存 ${def.name} · ${model} (${maskApiKey(apiKey)})`));
151
+ setStoredProvider(id, { apiKey: storedApiKey, model });
152
+ ok(t(locale, `Guardado ${def.name} · ${model} (${maskApiKey(storedApiKey)})`, `Saved ${def.name} · ${model} (${maskApiKey(storedApiKey)})`, `已保存 ${def.name} · ${model} (${maskApiKey(storedApiKey)})`));
152
153
  return true;
153
154
  }
154
155
  function resolveRowTarget(rows, target) {
@@ -7,7 +7,9 @@ export type EkzSkill = {
7
7
  declare const SKILL_IDS: readonly ["ekz-connect", "ekz-payment-core-architecture", "ekz-ekwanza-provider-adapter", "ekz-webhook-normalization", "ekz-one-time-product-payments", "ekz-ticket-invite-selling", "ekz-subscription-billing", "ekz-overage-billing", "ekz-integration-playbook", "ekz-data-layer-design", "ekz-data-postgres", "ekz-data-mysql", "ekz-data-sqlite", "ekz-data-mongo", "ekz-sdk-cli"];
8
8
  export type EkzSkillId = (typeof SKILL_IDS)[number];
9
9
  export declare function loadAllSkills(): EkzSkill[];
10
- export declare function selectSkillsForTask(task: string, scanRules?: string[]): EkzSkill[];
10
+ export declare function selectSkillsForTask(task: string, scanRules?: string[], options?: {
11
+ lite?: boolean;
12
+ }): EkzSkill[];
11
13
  export declare function formatSkillsForPrompt(skills: EkzSkill[]): string;
12
14
  export declare function listSkillIds(): string[];
13
15
  /** @deprecated use formatSkillsForPrompt(selectSkillsForTask(...)) */
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,QAAA,MAAM,SAAS,yXAgBL,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAkIpD,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAe1C;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAoClF;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAIhE;AAED,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAQvC;AAED,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,6IAGiB,CAAC"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/lib/skills.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,QAAA,MAAM,SAAS,yXAgBL,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAoIpD,wBAAgB,aAAa,IAAI,QAAQ,EAAE,CAkB1C;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAAE,EACpB,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,QAAQ,EAAE,CA8CZ;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAIhE;AAED,wBAAgB,YAAY,IAAI,MAAM,EAAE,CAQvC;AAED,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,6IAGiB,CAAC"}
@@ -132,7 +132,10 @@ function loadSkillFromDir(root, id) {
132
132
  const { name, description, body } = parseFrontmatter(raw);
133
133
  return { id, name: name || id, description, body };
134
134
  }
135
+ let cachedSkills = null;
135
136
  export function loadAllSkills() {
137
+ if (cachedSkills)
138
+ return cachedSkills;
136
139
  const roots = resolveSkillsRoots();
137
140
  const out = [];
138
141
  for (const id of SKILL_IDS) {
@@ -144,12 +147,16 @@ export function loadAllSkills() {
144
147
  }
145
148
  }
146
149
  }
150
+ cachedSkills = out;
147
151
  return out;
148
152
  }
149
- export function selectSkillsForTask(task, scanRules) {
153
+ export function selectSkillsForTask(task, scanRules, options) {
150
154
  const all = loadAllSkills();
151
155
  const byId = new Map(all.map((s) => [s.id, s]));
152
156
  const selected = new Set(["ekz-connect"]);
157
+ if (options?.lite) {
158
+ return [byId.get("ekz-connect")].filter((s) => Boolean(s));
159
+ }
153
160
  const haystack = [task, ...(scanRules ?? [])].join("\n");
154
161
  for (const { pattern, skills } of TASK_SKILL_MAP) {
155
162
  if (pattern.test(haystack)) {
@@ -164,14 +171,17 @@ export function selectSkillsForTask(task, scanRules) {
164
171
  selected.has("ekz-integration-playbook")) {
165
172
  selected.add("ekz-data-layer-design");
166
173
  }
167
- // Default bundle when nothing matched beyond orchestrator
174
+ // Full integration bundle only when the task actually needs it.
168
175
  if (selected.size === 1) {
169
- selected.add("ekz-payment-core-architecture");
170
- selected.add("ekz-ekwanza-provider-adapter");
171
- selected.add("ekz-webhook-normalization");
172
- selected.add("ekz-integration-playbook");
173
- selected.add("ekz-data-layer-design");
174
- selected.add("ekz-sdk-cli");
176
+ const needsBundle = /ekwanza|webhook|gpo|emis|ticket|checkout|integr|fix|audit|payment|rail|sdk|connect|scan|doctor|env/i.test(task);
177
+ if (needsBundle) {
178
+ selected.add("ekz-payment-core-architecture");
179
+ selected.add("ekz-ekwanza-provider-adapter");
180
+ selected.add("ekz-webhook-normalization");
181
+ selected.add("ekz-integration-playbook");
182
+ selected.add("ekz-data-layer-design");
183
+ selected.add("ekz-sdk-cli");
184
+ }
175
185
  }
176
186
  return SKILL_IDS.filter((id) => selected.has(id))
177
187
  .map((id) => byId.get(id))
@@ -0,0 +1,5 @@
1
+ import type { EkzLocale } from "./locale.js";
2
+ export declare function formatTerminalDelta(text: string): string;
3
+ export declare function formatTerminalAnswer(text: string): string;
4
+ export declare function terminalStyleInstruction(locale: EkzLocale): string;
5
+ //# sourceMappingURL=terminal-answer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-answer.d.ts","sourceRoot":"","sources":["../../src/lib/terminal-answer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAkB7C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKxD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASzD;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CA4BlE"}
@@ -0,0 +1,55 @@
1
+ import { c } from "./theme.js";
2
+ const EMOJI_RE = /[\p{Extended_Pictographic}\uFE0F\u200D]/gu;
3
+ function stripEmoji(text) {
4
+ return text.replace(EMOJI_RE, "");
5
+ }
6
+ function bold(text) {
7
+ return process.stdout.isTTY ? `${c.bold}${text}${c.reset}` : text;
8
+ }
9
+ function heading(text) {
10
+ const clean = text.trim().replace(/[::]\s*$/, "");
11
+ return bold(clean);
12
+ }
13
+ export function formatTerminalDelta(text) {
14
+ return stripEmoji(text)
15
+ .replace(/\*\*/g, "")
16
+ .replace(/^#{1,6}\s+/gm, "")
17
+ .replace(/[ \t]+\n/g, "\n");
18
+ }
19
+ export function formatTerminalAnswer(text) {
20
+ return stripEmoji(text)
21
+ .replace(/\r\n/g, "\n")
22
+ .replace(/^#{1,6}\s*(.+)$/gm, (_match, title) => `${heading(title)}:`)
23
+ .replace(/\*\*([^*\n]+)\*\*/g, (_match, value) => bold(value))
24
+ .replace(/^\s*[-*]\s+/gm, "- ")
25
+ .replace(/[ \t]+\n/g, "\n")
26
+ .replace(/\n{3,}/g, "\n\n")
27
+ .trim();
28
+ }
29
+ export function terminalStyleInstruction(locale) {
30
+ if (locale === "zh") {
31
+ return [
32
+ "## 终端输出格式",
33
+ "不要使用 emoji。",
34
+ "不要使用 Markdown 粗体标记,例如 **text**。",
35
+ "标题请用简短纯文本,并以冒号结尾。",
36
+ "保持回答紧凑,优先使用短句和普通列表。",
37
+ ].join("\n");
38
+ }
39
+ if (locale === "pt") {
40
+ return [
41
+ "## Formato para terminal",
42
+ "Não uses emojis.",
43
+ "Não uses marcadores Markdown de negrito como **texto**.",
44
+ "Usa títulos curtos em texto simples, terminados com dois-pontos.",
45
+ "Mantém a resposta compacta, com frases curtas e listas simples.",
46
+ ].join("\n");
47
+ }
48
+ return [
49
+ "## Terminal output format",
50
+ "Do not use emoji.",
51
+ "Do not use Markdown bold markers like **text**.",
52
+ "Use short plain-text headings ending with a colon.",
53
+ "Keep the answer compact, with short sentences and simple lists.",
54
+ ].join("\n");
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekzs/cli",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "CLI agent for e-Kwanza v2.4 — health checks, code scan, webhook tests, AI assistance",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "keywords": ["ekwanza", "ekzs", "cli", "payments", "angola", "cursor", "agent"],
38
38
  "author": "Alberto Moisés",
39
- "license": "UNLICENSED",
39
+ "license": "MIT",
40
40
  "repository": {
41
41
  "type": "git",
42
42
  "url": "git+https://github.com/Almpro3/ekz.git",