@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.
- package/README.md +148 -0
- package/dist/commands/agent.d.ts +31 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +55 -0
- package/dist/commands/ask.d.ts +20 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +154 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +44 -0
- package/dist/commands/health.d.ts +2 -0
- package/dist/commands/health.d.ts.map +1 -0
- package/dist/commands/health.js +28 -0
- package/dist/commands/local-agent.d.ts +19 -0
- package/dist/commands/local-agent.d.ts.map +1 -0
- package/dist/commands/local-agent.js +450 -0
- package/dist/commands/scan.d.ts +11 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +119 -0
- package/dist/commands/webhook.d.ts +10 -0
- package/dist/commands/webhook.d.ts.map +1 -0
- package/dist/commands/webhook.js +42 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +185 -0
- package/dist/lib/banner.d.ts +10 -0
- package/dist/lib/banner.d.ts.map +1 -0
- package/dist/lib/banner.js +26 -0
- package/dist/lib/commands-i18n.d.ts +20 -0
- package/dist/lib/commands-i18n.d.ts.map +1 -0
- package/dist/lib/commands-i18n.js +157 -0
- package/dist/lib/composer-model.d.ts +10 -0
- package/dist/lib/composer-model.d.ts.map +1 -0
- package/dist/lib/composer-model.js +15 -0
- package/dist/lib/context.d.ts +12 -0
- package/dist/lib/context.d.ts.map +1 -0
- package/dist/lib/context.js +56 -0
- package/dist/lib/doctor-quiet.d.ts +11 -0
- package/dist/lib/doctor-quiet.d.ts.map +1 -0
- package/dist/lib/doctor-quiet.js +39 -0
- package/dist/lib/env.d.ts +18 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +66 -0
- package/dist/lib/help.d.ts +10 -0
- package/dist/lib/help.d.ts.map +1 -0
- package/dist/lib/help.js +140 -0
- package/dist/lib/locale.d.ts +38 -0
- package/dist/lib/locale.d.ts.map +1 -0
- package/dist/lib/locale.js +189 -0
- package/dist/lib/mode.d.ts +11 -0
- package/dist/lib/mode.d.ts.map +1 -0
- package/dist/lib/mode.js +29 -0
- package/dist/lib/output.d.ts +7 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +18 -0
- package/dist/lib/preferences.d.ts +9 -0
- package/dist/lib/preferences.d.ts.map +1 -0
- package/dist/lib/preferences.js +35 -0
- package/dist/lib/redact.d.ts +3 -0
- package/dist/lib/redact.d.ts.map +1 -0
- package/dist/lib/redact.js +32 -0
- package/dist/lib/scan-quiet.d.ts +4 -0
- package/dist/lib/scan-quiet.d.ts.map +1 -0
- package/dist/lib/scan-quiet.js +4 -0
- package/dist/lib/scope.d.ts +5 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +61 -0
- package/dist/lib/session.d.ts +31 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/session.js +101 -0
- package/dist/lib/shell.d.ts +18 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +214 -0
- package/dist/lib/skill.d.ts +3 -0
- package/dist/lib/skill.d.ts.map +1 -0
- package/dist/lib/skill.js +2 -0
- package/dist/lib/skills.d.ts +16 -0
- package/dist/lib/skills.d.ts.map +1 -0
- package/dist/lib/skills.js +199 -0
- package/dist/lib/theme.d.ts +23 -0
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/lib/theme.js +40 -0
- package/dist/lib/ui/ascii-art.d.ts +10 -0
- package/dist/lib/ui/ascii-art.d.ts.map +1 -0
- package/dist/lib/ui/ascii-art.js +55 -0
- package/dist/lib/ui/layout.d.ts +19 -0
- package/dist/lib/ui/layout.d.ts.map +1 -0
- package/dist/lib/ui/layout.js +46 -0
- package/dist/lib/ui/logo.d.ts +3 -0
- package/dist/lib/ui/logo.d.ts.map +1 -0
- package/dist/lib/ui/logo.js +8 -0
- package/dist/lib/ui/prompt.d.ts +6 -0
- package/dist/lib/ui/prompt.d.ts.map +1 -0
- package/dist/lib/ui/prompt.js +75 -0
- package/dist/lib/ui/splash.d.ts +15 -0
- package/dist/lib/ui/splash.d.ts.map +1 -0
- package/dist/lib/ui/splash.js +121 -0
- package/package.json +48 -0
- package/skills/ekz-connect/SKILL.md +99 -0
- package/skills/ekz-data-layer-design/SKILL.md +199 -0
- package/skills/ekz-data-mongo/SKILL.md +341 -0
- package/skills/ekz-data-mysql/SKILL.md +245 -0
- package/skills/ekz-data-postgres/SKILL.md +257 -0
- package/skills/ekz-data-sqlite/SKILL.md +261 -0
- package/skills/ekz-ekwanza-provider-adapter/SKILL.md +91 -0
- package/skills/ekz-integration-playbook/SKILL.md +122 -0
- package/skills/ekz-one-time-product-payments/SKILL.md +91 -0
- package/skills/ekz-overage-billing/SKILL.md +68 -0
- package/skills/ekz-payment-core-architecture/SKILL.md +121 -0
- package/skills/ekz-sdk-cli/SKILL.md +82 -0
- package/skills/ekz-subscription-billing/SKILL.md +120 -0
- package/skills/ekz-ticket-invite-selling/SKILL.md +64 -0
- 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 @@
|
|
|
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 @@
|
|
|
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"}
|