@ekzs/cli 0.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.
Files changed (113) hide show
  1. package/README.md +148 -0
  2. package/dist/commands/agent.d.ts +31 -0
  3. package/dist/commands/agent.d.ts.map +1 -0
  4. package/dist/commands/agent.js +55 -0
  5. package/dist/commands/ask.d.ts +20 -0
  6. package/dist/commands/ask.d.ts.map +1 -0
  7. package/dist/commands/ask.js +154 -0
  8. package/dist/commands/doctor.d.ts +3 -0
  9. package/dist/commands/doctor.d.ts.map +1 -0
  10. package/dist/commands/doctor.js +44 -0
  11. package/dist/commands/health.d.ts +2 -0
  12. package/dist/commands/health.d.ts.map +1 -0
  13. package/dist/commands/health.js +28 -0
  14. package/dist/commands/local-agent.d.ts +19 -0
  15. package/dist/commands/local-agent.d.ts.map +1 -0
  16. package/dist/commands/local-agent.js +450 -0
  17. package/dist/commands/scan.d.ts +11 -0
  18. package/dist/commands/scan.d.ts.map +1 -0
  19. package/dist/commands/scan.js +119 -0
  20. package/dist/commands/webhook.d.ts +10 -0
  21. package/dist/commands/webhook.d.ts.map +1 -0
  22. package/dist/commands/webhook.js +42 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +185 -0
  26. package/dist/lib/banner.d.ts +10 -0
  27. package/dist/lib/banner.d.ts.map +1 -0
  28. package/dist/lib/banner.js +26 -0
  29. package/dist/lib/commands-i18n.d.ts +20 -0
  30. package/dist/lib/commands-i18n.d.ts.map +1 -0
  31. package/dist/lib/commands-i18n.js +157 -0
  32. package/dist/lib/composer-model.d.ts +10 -0
  33. package/dist/lib/composer-model.d.ts.map +1 -0
  34. package/dist/lib/composer-model.js +15 -0
  35. package/dist/lib/context.d.ts +12 -0
  36. package/dist/lib/context.d.ts.map +1 -0
  37. package/dist/lib/context.js +56 -0
  38. package/dist/lib/doctor-quiet.d.ts +11 -0
  39. package/dist/lib/doctor-quiet.d.ts.map +1 -0
  40. package/dist/lib/doctor-quiet.js +39 -0
  41. package/dist/lib/env.d.ts +18 -0
  42. package/dist/lib/env.d.ts.map +1 -0
  43. package/dist/lib/env.js +66 -0
  44. package/dist/lib/help.d.ts +10 -0
  45. package/dist/lib/help.d.ts.map +1 -0
  46. package/dist/lib/help.js +140 -0
  47. package/dist/lib/locale.d.ts +38 -0
  48. package/dist/lib/locale.d.ts.map +1 -0
  49. package/dist/lib/locale.js +189 -0
  50. package/dist/lib/mode.d.ts +11 -0
  51. package/dist/lib/mode.d.ts.map +1 -0
  52. package/dist/lib/mode.js +29 -0
  53. package/dist/lib/output.d.ts +7 -0
  54. package/dist/lib/output.d.ts.map +1 -0
  55. package/dist/lib/output.js +18 -0
  56. package/dist/lib/preferences.d.ts +9 -0
  57. package/dist/lib/preferences.d.ts.map +1 -0
  58. package/dist/lib/preferences.js +35 -0
  59. package/dist/lib/redact.d.ts +3 -0
  60. package/dist/lib/redact.d.ts.map +1 -0
  61. package/dist/lib/redact.js +32 -0
  62. package/dist/lib/scan-quiet.d.ts +4 -0
  63. package/dist/lib/scan-quiet.d.ts.map +1 -0
  64. package/dist/lib/scan-quiet.js +4 -0
  65. package/dist/lib/scope.d.ts +5 -0
  66. package/dist/lib/scope.d.ts.map +1 -0
  67. package/dist/lib/scope.js +61 -0
  68. package/dist/lib/session.d.ts +31 -0
  69. package/dist/lib/session.d.ts.map +1 -0
  70. package/dist/lib/session.js +101 -0
  71. package/dist/lib/shell.d.ts +18 -0
  72. package/dist/lib/shell.d.ts.map +1 -0
  73. package/dist/lib/shell.js +214 -0
  74. package/dist/lib/skill.d.ts +3 -0
  75. package/dist/lib/skill.d.ts.map +1 -0
  76. package/dist/lib/skill.js +2 -0
  77. package/dist/lib/skills.d.ts +16 -0
  78. package/dist/lib/skills.d.ts.map +1 -0
  79. package/dist/lib/skills.js +199 -0
  80. package/dist/lib/theme.d.ts +23 -0
  81. package/dist/lib/theme.d.ts.map +1 -0
  82. package/dist/lib/theme.js +40 -0
  83. package/dist/lib/ui/ascii-art.d.ts +10 -0
  84. package/dist/lib/ui/ascii-art.d.ts.map +1 -0
  85. package/dist/lib/ui/ascii-art.js +55 -0
  86. package/dist/lib/ui/layout.d.ts +19 -0
  87. package/dist/lib/ui/layout.d.ts.map +1 -0
  88. package/dist/lib/ui/layout.js +46 -0
  89. package/dist/lib/ui/logo.d.ts +3 -0
  90. package/dist/lib/ui/logo.d.ts.map +1 -0
  91. package/dist/lib/ui/logo.js +8 -0
  92. package/dist/lib/ui/prompt.d.ts +6 -0
  93. package/dist/lib/ui/prompt.d.ts.map +1 -0
  94. package/dist/lib/ui/prompt.js +75 -0
  95. package/dist/lib/ui/splash.d.ts +15 -0
  96. package/dist/lib/ui/splash.d.ts.map +1 -0
  97. package/dist/lib/ui/splash.js +121 -0
  98. package/package.json +48 -0
  99. package/skills/ekz-connect/SKILL.md +99 -0
  100. package/skills/ekz-data-layer-design/SKILL.md +199 -0
  101. package/skills/ekz-data-mongo/SKILL.md +341 -0
  102. package/skills/ekz-data-mysql/SKILL.md +245 -0
  103. package/skills/ekz-data-postgres/SKILL.md +257 -0
  104. package/skills/ekz-data-sqlite/SKILL.md +261 -0
  105. package/skills/ekz-ekwanza-provider-adapter/SKILL.md +91 -0
  106. package/skills/ekz-integration-playbook/SKILL.md +122 -0
  107. package/skills/ekz-one-time-product-payments/SKILL.md +91 -0
  108. package/skills/ekz-overage-billing/SKILL.md +68 -0
  109. package/skills/ekz-payment-core-architecture/SKILL.md +121 -0
  110. package/skills/ekz-sdk-cli/SKILL.md +82 -0
  111. package/skills/ekz-subscription-billing/SKILL.md +120 -0
  112. package/skills/ekz-ticket-invite-selling/SKILL.md +64 -0
  113. package/skills/ekz-webhook-normalization/SKILL.md +88 -0
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # @ekzs/cli
2
+
3
+ **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.
4
+
5
+ Runs **in your repo** with Composer 2.5 Fast. Edits files directly.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @ekzs/cli
11
+ # or from this monorepo:
12
+ npm run build --prefix packages/cli
13
+ ```
14
+
15
+ ## Setup
16
+
17
+ ```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
21
+ ```
22
+
23
+ Loads `.env.local` / `.env` from your project automatically.
24
+
25
+ ### Run from this repo
26
+
27
+ ```bash
28
+ npm run build:cli
29
+
30
+ # Option A — wrapper (no global install)
31
+ ./ekz # Git Bash / macOS / Linux
32
+ .\ekz # PowerShell / cmd (resolves to ekz.cmd)
33
+ npx ekz # any shell, from repo root
34
+
35
+ # Option B — npm script
36
+ npm run ekz # interactive REPL
37
+ npm run ekz -- agent "fix webhook"
38
+
39
+ # Option C — global command (once)
40
+ npm run setup:cli # build + npm link
41
+ ekz # works everywhere
42
+ ```
43
+
44
+ ### Windows / PowerShell
45
+
46
+ From the repo root (after `npm run build:cli`):
47
+
48
+ ```powershell
49
+ .\ekz # same as ekz.cmd — no extension needed
50
+ npx ekz # works in PowerShell, cmd, Git Bash
51
+ npm run setup:cli # then `ekz` works globally
52
+ ```
53
+
54
+ Shell type is **detected at launch** (Cursor terminal settings, Git Bash, or Windows default PowerShell) and injected into every agent turn. Override with `EKZ_AGENT_SHELL=powershell|bash|cmd` if needed.
55
+
56
+ When testing HTTP yourself in PowerShell, use `curl.exe -s "http://localhost:3000/..."` — bare `curl` is an alias for `Invoke-WebRequest` and will prompt for `Uri:`.
57
+
58
+ Prefer built-in CLI helpers over manual curls: `ekz doctor`, `ekz health`, `ekz webhook <url>`.
59
+
60
+ ## Commands
61
+
62
+ | Command | Description |
63
+ |---------|-------------|
64
+ | `ekz` / `ekz agent` | Interactive REPL (default mode: agent) |
65
+ | `ekz --mode plan` | Plan before editing files |
66
+ | `ekz --mode ask` | Remote Q&A, no file edits |
67
+ | `ekz agent "fix webhook"` | One-shot task with file edits |
68
+ | `ekz fix` | Full integration audit + auto-fix pass |
69
+ | `ekz ask "…"` | Remote Q&A only (Ekz Connect API) |
70
+ | `ekz ask -i` | Interactive ask REPL |
71
+ | `ekz agent --resume` | Continue last session (`.ekz/session.json`) |
72
+ | `ekz doctor` | Env + OAuth health (free) |
73
+ | `ekz health` | OAuth + rail health (free) |
74
+ | `ekz scan` | Lint common mistakes (free) |
75
+ | `ekz webhook <url>` | POST sample webhook (free) |
76
+
77
+ ### REPL shortcuts
78
+
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`.
80
+
81
+ ## Examples
82
+
83
+ ```bash
84
+ # Interactive — stay in your project and iterate
85
+ ekz agent
86
+
87
+ # One-shot fix
88
+ ekz fix
89
+
90
+ # With extra context
91
+ ekz fix "Ticket webhook returns 401 on x-signature"
92
+
93
+ # Plan before editing
94
+ ekz --mode plan "Add emis_ref rail to checkout"
95
+ # or shorthand:
96
+ ekz agent --plan "Add emis_ref rail to checkout"
97
+
98
+ # Ask mode (no local edits)
99
+ ekz --mode ask "What env vars am I missing for Ticket?"
100
+
101
+ # Resume yesterday's session
102
+ ekz agent --resume
103
+ ```
104
+
105
+ ## Billing
106
+
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.
110
+
111
+ ## Session file
112
+
113
+ `.ekz/session.json` stores agent ID for `--resume`. Add `.ekz/` to `.gitignore`.
114
+
115
+ ## Agent skills
116
+
117
+ Ekz ships a skill pack under `packages/cli/skills/` (orchestrator + architecture sub-skills). On build, skills sync to `.cursor/skills/` so Cursor picks them up locally.
118
+
119
+ | Skill | Use when |
120
+ |-------|----------|
121
+ | `ekz-connect` | Orchestrator — always loaded |
122
+ | `ekz-payment-core-architecture` | Payment primitives, state machines, ledger, idempotency |
123
+ | `ekz-ekwanza-provider-adapter` | e-Kwanza auth, config, GPO, EMIS ref, Ticket API |
124
+ | `ekz-webhook-normalization` | Callback normalization, x-signature, dedupe, routing |
125
+ | `ekz-one-time-product-payments` | Products, orders, invoices, checkout links, fulfillment |
126
+ | `ekz-ticket-invite-selling` | Reservations, invite/ticket issuance, QR/code ownership |
127
+ | `ekz-subscription-billing` | Recurring one-time payment requests, grace, entitlements |
128
+ | `ekz-overage-billing` | Metered usage, overage charges, settlement |
129
+ | `ekz-integration-playbook` | Adapting payment patterns into any existing codebase |
130
+ | `ekz-sdk-cli` | `@ekzs/connect`, `ekz doctor`, CLI |
131
+
132
+ ```bash
133
+ npm run sync:skills --prefix packages/cli # copy to .cursor/skills/
134
+ ```
135
+
136
+ The local agent loads project skills via `settingSources: ["project"]` and injects task-relevant skills into the prompt.
137
+
138
+ ### Language
139
+
140
+ Default replies are in **Portuguese (Angola)**. Switch anytime in the REPL:
141
+
142
+ ```
143
+ /lang pt
144
+ /lang en
145
+ /ajuda
146
+ ```
147
+
148
+ Or set `EKZ_LANG=pt` in `.env.local`, or start with `./ekz --lang en`.
@@ -0,0 +1,31 @@
1
+ import { resolveQuestion } from "./ask.js";
2
+ export { resolveQuestion };
3
+ export declare function runAgentCommand(opts: {
4
+ cwd: string;
5
+ question?: string;
6
+ apiKey?: string;
7
+ askApiKey?: string;
8
+ askApiUrl?: string;
9
+ askOffline?: boolean;
10
+ resume?: boolean;
11
+ fresh?: boolean;
12
+ mode?: string;
13
+ plan?: boolean;
14
+ interactive?: boolean;
15
+ locale?: string;
16
+ }): Promise<void>;
17
+ export declare function runFixCommand(opts: {
18
+ cwd: string;
19
+ apiKey?: string;
20
+ askApiKey?: string;
21
+ askApiUrl?: string;
22
+ askOffline?: boolean;
23
+ resume?: boolean;
24
+ fresh?: boolean;
25
+ mode?: string;
26
+ plan?: boolean;
27
+ interactive?: boolean;
28
+ extra?: string;
29
+ locale?: string;
30
+ }): Promise<void>;
31
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,55 @@
1
+ import { resolveQuestion } from "./ask.js";
2
+ import { FIX_DEFAULT_TASK } from "../lib/context.js";
3
+ import { runInteractiveAgent, runLocalAgent } from "./local-agent.js";
4
+ export { resolveQuestion };
5
+ export async function runAgentCommand(opts) {
6
+ const mode = opts.mode;
7
+ if (!opts.question?.trim()) {
8
+ await runInteractiveAgent({
9
+ cwd: opts.cwd,
10
+ apiKey: opts.apiKey,
11
+ askApiKey: opts.askApiKey,
12
+ askApiUrl: opts.askApiUrl,
13
+ askOffline: opts.askOffline,
14
+ resume: opts.resume,
15
+ fresh: opts.fresh,
16
+ mode,
17
+ plan: opts.plan,
18
+ locale: opts.locale,
19
+ });
20
+ return;
21
+ }
22
+ await runLocalAgent({
23
+ cwd: opts.cwd,
24
+ task: opts.question.trim(),
25
+ apiKey: opts.apiKey,
26
+ askApiKey: opts.askApiKey,
27
+ askApiUrl: opts.askApiUrl,
28
+ askOffline: opts.askOffline,
29
+ resume: opts.resume,
30
+ fresh: opts.fresh,
31
+ mode,
32
+ plan: opts.plan,
33
+ interactive: opts.interactive,
34
+ locale: opts.locale,
35
+ });
36
+ }
37
+ export async function runFixCommand(opts) {
38
+ const task = opts.extra?.trim()
39
+ ? `${FIX_DEFAULT_TASK}\n\nAdditional context: ${opts.extra}`
40
+ : FIX_DEFAULT_TASK;
41
+ await runLocalAgent({
42
+ cwd: opts.cwd,
43
+ task,
44
+ apiKey: opts.apiKey,
45
+ askApiKey: opts.askApiKey,
46
+ askApiUrl: opts.askApiUrl,
47
+ askOffline: opts.askOffline,
48
+ resume: opts.resume,
49
+ fresh: opts.fresh,
50
+ mode: opts.mode,
51
+ plan: opts.plan,
52
+ interactive: opts.interactive,
53
+ locale: opts.locale,
54
+ });
55
+ }
@@ -0,0 +1,20 @@
1
+ export type AskOptions = {
2
+ cwd: string;
3
+ question: string;
4
+ apiKey?: string;
5
+ apiUrl?: string;
6
+ offline?: boolean;
7
+ quiet?: boolean;
8
+ };
9
+ /** Remote Q&A via Ekz Connect API (no local edits). */
10
+ export declare function executeAskTurn(options: AskOptions): Promise<boolean>;
11
+ export declare function runAsk(options: Omit<AskOptions, "quiet">): Promise<void>;
12
+ export declare function runInteractiveAsk(opts: {
13
+ cwd: string;
14
+ locale?: string;
15
+ apiKey?: string;
16
+ apiUrl?: string;
17
+ offline?: boolean;
18
+ }): Promise<void>;
19
+ export declare function resolveQuestion(arg?: string): Promise<string | undefined>;
20
+ //# sourceMappingURL=ask.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,154 @@
1
+ import { printHelp, printWelcome } from "../lib/banner.js";
2
+ import { resolveReplMetaCommand } from "../lib/commands-i18n.js";
3
+ import { buildBootstrapContext } from "../lib/context.js";
4
+ import { parseLocale, uiStrings } from "../lib/locale.js";
5
+ import { parseMode } from "../lib/mode.js";
6
+ import { fail, heading, info, ok } from "../lib/output.js";
7
+ import { loadPreferences, savePreferences } from "../lib/preferences.js";
8
+ import { redactText } from "../lib/redact.js";
9
+ import { askWithPlaceholder, formatInputPrompt } from "../lib/ui/prompt.js";
10
+ import { inputPlaceholder } from "../lib/ui/splash.js";
11
+ function deterministicAnswer(question, ctx) {
12
+ const lines = ["## Ekz assistant (offline)", "", question, ""];
13
+ if (ctx.doctor.issues.length) {
14
+ lines.push("### Issues", ...ctx.doctor.issues.map((i) => `- ${i}`), "");
15
+ }
16
+ if (ctx.scan.length) {
17
+ lines.push("### Scan", ...ctx.scan.slice(0, 8).map((f) => `- \`${f.file}:${f.line}\` · ${f.message}`), "");
18
+ }
19
+ lines.push("### Next steps", "- Set EKZ_AGENT_API_KEY for remote answers", "- Or run `ekz --mode agent` for local file edits");
20
+ return lines.join("\n");
21
+ }
22
+ /** Remote Q&A via Ekz Connect API (no local edits). */
23
+ 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
+ if (!quiet)
30
+ info("Ask mode (offline) · no file edits.\n");
31
+ console.log("\n" + deterministicAnswer(question, ctx));
32
+ return true;
33
+ }
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;
49
+ }
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;
56
+ }
57
+ export async function runAsk(options) {
58
+ 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 });
61
+ if (!okResult)
62
+ process.exitCode = 1;
63
+ }
64
+ function handleAskMeta(line, locale) {
65
+ const parts = line.trim().split(/\s+/);
66
+ const cmd = resolveReplMetaCommand(parts[0] ?? "");
67
+ if (cmd === "help") {
68
+ printHelp(locale, "ask");
69
+ return { kind: "handled" };
70
+ }
71
+ if (cmd === "lang") {
72
+ const next = parseLocale(parts[1]);
73
+ if (!next) {
74
+ fail(uiStrings(locale).langInvalid);
75
+ return { kind: "handled" };
76
+ }
77
+ return { kind: "locale", locale: next };
78
+ }
79
+ if (cmd === "mode") {
80
+ const next = parseMode(parts[1]);
81
+ if (!next) {
82
+ fail(locale === "pt"
83
+ ? "Modo inválido. Usa: /mode agent|plan|ask"
84
+ : locale === "zh"
85
+ ? "无效模式。请使用: /mode agent|plan|ask"
86
+ : "Invalid mode. Use: /mode agent|plan|ask");
87
+ return { kind: "handled" };
88
+ }
89
+ return { kind: "mode", mode: next };
90
+ }
91
+ if (cmd === "exit" || cmd === "quit" || cmd === "sair" || cmd === "退出") {
92
+ return { kind: "exit" };
93
+ }
94
+ return null;
95
+ }
96
+ export async function runInteractiveAsk(opts) {
97
+ const prefs = loadPreferences(opts.cwd, opts.locale, "ask");
98
+ let currentLocale = prefs.locale;
99
+ printWelcome({
100
+ cwd: opts.cwd,
101
+ locale: currentLocale,
102
+ mode: "ask",
103
+ });
104
+ let turn = 0;
105
+ while (true) {
106
+ const line = (await askWithPlaceholder({
107
+ prompt: formatInputPrompt(),
108
+ placeholder: inputPlaceholder(currentLocale, turn),
109
+ })).trim();
110
+ if (!line)
111
+ continue;
112
+ const meta = handleAskMeta(line, currentLocale);
113
+ if (meta?.kind === "handled")
114
+ continue;
115
+ if (meta?.kind === "exit")
116
+ break;
117
+ if (meta?.kind === "locale") {
118
+ currentLocale = meta.locale;
119
+ savePreferences(opts.cwd, { locale: meta.locale });
120
+ continue;
121
+ }
122
+ if (meta?.kind === "mode" && meta.mode !== "ask") {
123
+ savePreferences(opts.cwd, { mode: meta.mode });
124
+ info(currentLocale === "pt"
125
+ ? `Reinicia com: ekz --mode ${meta.mode}`
126
+ : currentLocale === "zh"
127
+ ? `请重新运行: ekz --mode ${meta.mode}`
128
+ : `Restart with: ekz --mode ${meta.mode}`);
129
+ continue;
130
+ }
131
+ console.log("");
132
+ await executeAskTurn({
133
+ cwd: opts.cwd,
134
+ question: line,
135
+ apiKey: opts.apiKey,
136
+ apiUrl: opts.apiUrl,
137
+ offline: opts.offline,
138
+ quiet: true,
139
+ });
140
+ console.log("");
141
+ turn++;
142
+ }
143
+ ok(uiStrings(currentLocale).goodbye);
144
+ }
145
+ export async function resolveQuestion(arg) {
146
+ if (arg?.trim())
147
+ return arg.trim();
148
+ if (process.stdin.isTTY)
149
+ return undefined;
150
+ const chunks = [];
151
+ for await (const chunk of process.stdin)
152
+ chunks.push(chunk);
153
+ return redactText(chunks.join("")).trim() || "Help me fix my e-Kwanza integration";
154
+ }
@@ -0,0 +1,3 @@
1
+ import type { DoctorResult } from "../lib/doctor-quiet.js";
2
+ export declare function runDoctor(cwd: string): Promise<DoctorResult>;
3
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAgClD"}
@@ -0,0 +1,44 @@
1
+ import { collectDoctorResult } from "../lib/doctor-quiet.js";
2
+ import { fail, heading, info, ok, warn } from "../lib/output.js";
3
+ import { detectShellEnvironment } from "../lib/shell.js";
4
+ export async function runDoctor(cwd) {
5
+ const result = await collectDoctorResult(cwd);
6
+ heading("Ekz doctor");
7
+ info(`Project: ${cwd}`);
8
+ if (result.envFiles.length === 0)
9
+ warn("No .env or .env.local found");
10
+ else
11
+ ok(`Loaded ${result.envFiles.map((f) => f.split(/[/\\]/).pop()).join(", ")}`);
12
+ heading("Environment");
13
+ for (const issue of result.issues)
14
+ fail(issue);
15
+ for (const w of result.warnings)
16
+ warn(w);
17
+ if (result.rails.length)
18
+ ok(`Rails configured: ${result.rails.join(", ")}`);
19
+ if (result.health) {
20
+ heading("Live checks");
21
+ if (result.health.oauth)
22
+ ok("OAuth token OK");
23
+ else
24
+ fail("OAuth failed");
25
+ if (result.health.gpo)
26
+ ok("GPO configured");
27
+ if (result.health.emisRef)
28
+ ok("EMIS reference configured");
29
+ if (result.health.ticket)
30
+ ok("Ticket API configured");
31
+ if (result.health.configured)
32
+ ok("Integration ready");
33
+ }
34
+ heading("Summary");
35
+ if (result.issues.length === 0)
36
+ ok(`All checks passed (${result.warnings.length} warnings)`);
37
+ else
38
+ fail(`${result.issues.length} issue(s), ${result.warnings.length} warning(s)`);
39
+ const shell = detectShellEnvironment();
40
+ if (shell.isWindows && shell.agentShell !== "bash") {
41
+ info(`Agent shell: ${shell.agentShell} (${shell.source}) — use curl.exe or ekz webhook, not bare curl.`);
42
+ }
43
+ return result;
44
+ }
@@ -0,0 +1,2 @@
1
+ export declare function runHealth(cwd: string, asJson: boolean): Promise<import("@ekzs/connect").EkzCredentialHealth | null>;
2
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/commands/health.ts"],"names":[],"mappings":"AAIA,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,+DA2B3D"}
@@ -0,0 +1,28 @@
1
+ import { EkzConnect, configFromEnv } from "@ekzs/connect";
2
+ import { loadProjectEnv } from "../lib/env.js";
3
+ import { fail, heading, json, ok } from "../lib/output.js";
4
+ export async function runHealth(cwd, asJson) {
5
+ const { env } = loadProjectEnv(cwd);
6
+ const cfg = configFromEnv(env);
7
+ if (!cfg) {
8
+ fail("EKWANZA_* not configured");
9
+ process.exitCode = 1;
10
+ return null;
11
+ }
12
+ const client = new EkzConnect(cfg);
13
+ const health = await client.healthCheck();
14
+ if (asJson) {
15
+ json(health);
16
+ return health;
17
+ }
18
+ heading("Credential health");
19
+ ok(`env: ${health.env}`);
20
+ ok(`oauth: ${health.oauth ? "OK" : "FAIL"}`);
21
+ ok(`gpo: ${health.gpo}`);
22
+ ok(`emis_ref: ${health.emisRef}`);
23
+ ok(`ticket: ${health.ticket}`);
24
+ ok(`configured: ${health.configured}`);
25
+ if (!health.configured)
26
+ process.exitCode = 1;
27
+ return health;
28
+ }
@@ -0,0 +1,19 @@
1
+ import { type EkzMode } from "../lib/mode.js";
2
+ export type LocalAgentOptions = {
3
+ cwd: string;
4
+ task: string;
5
+ apiKey?: string;
6
+ askApiKey?: string;
7
+ askApiUrl?: string;
8
+ askOffline?: boolean;
9
+ resume?: boolean;
10
+ fresh?: boolean;
11
+ mode?: EkzMode;
12
+ plan?: boolean;
13
+ interactive?: boolean;
14
+ locale?: string;
15
+ resumeAgentId?: string;
16
+ };
17
+ export declare function runLocalAgent(opts: LocalAgentOptions): Promise<void>;
18
+ export declare function runInteractiveAgent(opts: Omit<LocalAgentOptions, "task" | "interactive">): Promise<void>;
19
+ //# sourceMappingURL=local-agent.d.ts.map
@@ -0,0 +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"}