@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
@@ -0,0 +1,40 @@
1
+ export const c = {
2
+ reset: "\x1b[0m",
3
+ bold: "\x1b[1m",
4
+ dim: "\x1b[2m",
5
+ orange: "\x1b[38;5;208m",
6
+ orangeBold: "\x1b[1;38;5;208m",
7
+ green: "\x1b[32m",
8
+ yellow: "\x1b[33m",
9
+ red: "\x1b[31m",
10
+ cyan: "\x1b[36m",
11
+ white: "\x1b[97m",
12
+ slate: "\x1b[38;5;245m",
13
+ };
14
+ export function muted(text) {
15
+ return `${c.dim}${c.slate}${text}${c.reset}`;
16
+ }
17
+ export function accent(text) {
18
+ return `${c.orangeBold}${text}${c.reset}`;
19
+ }
20
+ export function label(key, value) {
21
+ return ` ${muted(`${key}:`)} ${c.white}${value}${c.reset}`;
22
+ }
23
+ export function separator(width = 56) {
24
+ return muted(" " + "─".repeat(width));
25
+ }
26
+ export function badge(text) {
27
+ return `${c.orange} ${c.bold}${text}${c.reset}`;
28
+ }
29
+ export function toolRunning(name) {
30
+ return `\n ${c.orange}${c.bold}▸${c.reset} ${c.bold}${name}${c.reset} ${muted("…")}\n`;
31
+ }
32
+ export function toolDone(name) {
33
+ return ` ${c.green}${c.bold}✓${c.reset} ${muted(name)}\n`;
34
+ }
35
+ export function toolError(name) {
36
+ return ` ${c.red}${c.bold}✗${c.reset} ${name}\n`;
37
+ }
38
+ export function turnDivider() {
39
+ return `\n ${muted("─".repeat(54))}\n`;
40
+ }
@@ -0,0 +1,10 @@
1
+ /** 5-line block letters — gradient applied per line in splash */
2
+ export declare const BLOCK_EKZ: string[];
3
+ export declare const BLOCK_CONNECT: string[];
4
+ /** Compact CONNECT for narrower terminals */
5
+ export declare const BLOCK_CONNECT_SHORT: string[];
6
+ /** Payment mascot — é-Kwanza / Multicaixa vibe */
7
+ export declare const MASCOT: string[];
8
+ export declare function printHeroTitle(useShortConnect?: boolean): void;
9
+ export declare function printHeroSubtitle(text: string): void;
10
+ //# sourceMappingURL=ascii-art.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ascii-art.d.ts","sourceRoot":"","sources":["../../../src/lib/ui/ascii-art.ts"],"names":[],"mappings":"AAEA,iEAAiE;AACjE,eAAO,MAAM,SAAS,UAOrB,CAAC;AAEF,eAAO,MAAM,aAAa,UAOzB,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,mBAAmB,UAM/B,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,MAAM,UASlB,CAAC;AAIF,wBAAgB,cAAc,CAAC,eAAe,UAAQ,GAAG,IAAI,CAa5D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGpD"}
@@ -0,0 +1,55 @@
1
+ import { c } from "../theme.js";
2
+ /** 5-line block letters — gradient applied per line in splash */
3
+ export const BLOCK_EKZ = [
4
+ " ███████╗██╗ ██╗███████╗",
5
+ " ██╔════╝██║ ██╔╝╚══███╔╝",
6
+ " █████╗ █████╔╝ ███╔╝ ",
7
+ " ██╔══╝ ██╔═██╗ ███╔╝ ",
8
+ " ███████╗██║ ██╗███████╗",
9
+ " ╚══════╝╚═╝ ╚═╝╚══════╝",
10
+ ];
11
+ export const BLOCK_CONNECT = [
12
+ " ██████╗ ██████╗ ███╗ ██╗███╗ ██╗███████╗ ██████╗████████╗",
13
+ "██╔════╝██╔═══██╗████╗ ██║████╗ ██║██╔════╝██╔════╝╚══██╔══╝",
14
+ "██║ ██║ ██║██╔██╗ ██║██╔██╗ ██║█████╗ ██║ ██║ ",
15
+ "██║ ██║ ██║██║╚██╗██║██║╚██╗██║██╔══╝ ██║ ██║ ",
16
+ "╚██████╗╚██████╔╝██║ ╚████║██║ ╚████║███████╗╚██████╗ ██║ ",
17
+ " ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ",
18
+ ];
19
+ /** Compact CONNECT for narrower terminals */
20
+ export const BLOCK_CONNECT_SHORT = [
21
+ " ██████╗ ██████╗ ███╗ ██╗",
22
+ "██╔════╝██╔═══██╗████╗ ██║",
23
+ "██║ ██║ ██║██╔██╗ ██║",
24
+ "╚██████╗╚██████╔╝██║ ╚████║",
25
+ " ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝",
26
+ ];
27
+ /** Payment mascot — é-Kwanza / Multicaixa vibe */
28
+ export const MASCOT = [
29
+ " ┌──────────────┐",
30
+ " │ é-Kwanza v2 │",
31
+ " │ ▓▓▓▓▓▓▓▓▓▓ │",
32
+ " │ GPO · EMIS · │",
33
+ " │ Ticket │",
34
+ " └──────┬───────┘",
35
+ " Multicaixa",
36
+ " ◆ Kz ◆",
37
+ ];
38
+ const GRADIENT = ["\x1b[38;5;226m", "\x1b[38;5;220m", "\x1b[38;5;214m", "\x1b[38;5;208m", "\x1b[38;5;172m", "\x1b[38;5;130m"];
39
+ export function printHeroTitle(useShortConnect = false) {
40
+ const connect = useShortConnect ? BLOCK_CONNECT_SHORT : BLOCK_CONNECT;
41
+ console.log("");
42
+ for (let i = 0; i < BLOCK_EKZ.length; i++) {
43
+ const color = GRADIENT[Math.min(i, GRADIENT.length - 1)];
44
+ console.log(` ${color}${c.bold}${BLOCK_EKZ[i]}${c.reset}`);
45
+ }
46
+ for (let i = 0; i < connect.length; i++) {
47
+ const color = GRADIENT[Math.min(i + 1, GRADIENT.length - 1)];
48
+ console.log(` ${color}${connect[i]}${c.reset}`);
49
+ }
50
+ console.log("");
51
+ }
52
+ export function printHeroSubtitle(text) {
53
+ console.log(` ${c.dim}\x1b[38;5;245m${text}${c.reset}`);
54
+ console.log("");
55
+ }
@@ -0,0 +1,19 @@
1
+ /** Visible-length helpers for aligned terminal UI */
2
+ export declare function stripAnsi(text: string): string;
3
+ export declare function visibleLength(text: string): number;
4
+ export declare function padEndVisible(text: string, width: number): string;
5
+ export declare function truncateVisible(text: string, max: number): string;
6
+ export declare function mergeColumns(left: string[], right: string[], leftWidth: number, gap?: number): string[];
7
+ export declare const BOX: {
8
+ tl: string;
9
+ tr: string;
10
+ bl: string;
11
+ br: string;
12
+ h: string;
13
+ v: string;
14
+ cross: string;
15
+ teeR: string;
16
+ teeL: string;
17
+ };
18
+ export declare function renderBorderBox(title: string, bodyLines: string[], width?: number): void;
19
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/lib/ui/layout.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIjE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,EAAE,CAOlG;AAED,eAAO,MAAM,GAAG;;;;;;;;;;CAUf,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,KAAK,SAAK,GAAG,IAAI,CAWpF"}
@@ -0,0 +1,46 @@
1
+ /** Visible-length helpers for aligned terminal UI */
2
+ export function stripAnsi(text) {
3
+ return text.replace(/\x1b\[[0-9;]*m/g, "");
4
+ }
5
+ export function visibleLength(text) {
6
+ return stripAnsi(text).length;
7
+ }
8
+ export function padEndVisible(text, width) {
9
+ return text + " ".repeat(Math.max(0, width - visibleLength(text)));
10
+ }
11
+ export function truncateVisible(text, max) {
12
+ const plain = stripAnsi(text);
13
+ if (plain.length <= max)
14
+ return text;
15
+ return plain.slice(0, Math.max(0, max - 1)) + "…";
16
+ }
17
+ export function mergeColumns(left, right, leftWidth, gap = 2) {
18
+ const rows = Math.max(left.length, right.length);
19
+ const out = [];
20
+ for (let i = 0; i < rows; i++) {
21
+ out.push(padEndVisible(left[i] ?? "", leftWidth) + " ".repeat(gap) + (right[i] ?? ""));
22
+ }
23
+ return out;
24
+ }
25
+ export const BOX = {
26
+ tl: "╭",
27
+ tr: "╮",
28
+ bl: "╰",
29
+ br: "╯",
30
+ h: "─",
31
+ v: "│",
32
+ cross: "┼",
33
+ teeR: "├",
34
+ teeL: "┤",
35
+ };
36
+ export function renderBorderBox(title, bodyLines, width = 78) {
37
+ const inner = width - 2;
38
+ const titlePlain = ` ${title} `;
39
+ const dashCount = Math.max(0, inner - visibleLength(titlePlain));
40
+ const top = `${BOX.tl}${titlePlain}${BOX.h.repeat(dashCount)}${BOX.tr}`;
41
+ console.log(top);
42
+ for (const line of bodyLines) {
43
+ console.log(`${BOX.v}${padEndVisible(` ${line}`, inner)}${BOX.v}`);
44
+ }
45
+ console.log(`${BOX.bl}${BOX.h.repeat(inner)}${BOX.br}`);
46
+ }
@@ -0,0 +1,3 @@
1
+ /** Two-line wordmark — always reads EKZ / CONNECT */
2
+ export declare function renderLogoLines(): string[];
3
+ //# sourceMappingURL=logo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo.d.ts","sourceRoot":"","sources":["../../../src/lib/ui/logo.ts"],"names":[],"mappings":"AAMA,qDAAqD;AACrD,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C"}
@@ -0,0 +1,8 @@
1
+ import { c } from "../theme.js";
2
+ function colorLine(text, color) {
3
+ return `${c.bold}\x1b[38;5;${color}m${text}${c.reset}`;
4
+ }
5
+ /** Two-line wordmark — always reads EKZ / CONNECT */
6
+ export function renderLogoLines() {
7
+ return [colorLine("EKZ", 226), colorLine("CONNECT", 208)];
8
+ }
@@ -0,0 +1,6 @@
1
+ export declare function askWithPlaceholder(opts: {
2
+ prompt: string;
3
+ placeholder: string;
4
+ }): Promise<string>;
5
+ export declare function formatInputPrompt(): string;
6
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/lib/ui/prompt.ts"],"names":[],"mappings":"AAIA,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8ElB;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
@@ -0,0 +1,75 @@
1
+ import readline from "readline";
2
+ import { stripAnsi } from "./layout.js";
3
+ import { c, muted } from "../theme.js";
4
+ export async function askWithPlaceholder(opts) {
5
+ if (!process.stdin.isTTY) {
6
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
7
+ return new Promise((resolve) => {
8
+ rl.question(opts.prompt, (answer) => {
9
+ rl.close();
10
+ resolve(answer.trim() || stripAnsi(opts.placeholder).replace(/^Try /i, "").replace(/^Experimenta /i, ""));
11
+ });
12
+ });
13
+ }
14
+ return new Promise((resolve) => {
15
+ const { prompt, placeholder } = opts;
16
+ const stdin = process.stdin;
17
+ const stdout = process.stdout;
18
+ stdout.write(prompt);
19
+ stdout.write("\x1b[s");
20
+ stdout.write(muted(placeholder));
21
+ stdout.write("\x1b[u");
22
+ readline.emitKeypressEvents(stdin);
23
+ stdin.setRawMode(true);
24
+ stdin.resume();
25
+ let value = "";
26
+ let hintVisible = true;
27
+ const clearHint = () => {
28
+ if (!hintVisible)
29
+ return;
30
+ hintVisible = false;
31
+ stdout.write("\x1b[0K");
32
+ };
33
+ const cleanup = () => {
34
+ stdin.setRawMode(false);
35
+ stdin.removeListener("keypress", onKeypress);
36
+ stdout.write("\n");
37
+ };
38
+ const onKeypress = (_char, key) => {
39
+ if (!key)
40
+ return;
41
+ if (key.name === "return") {
42
+ cleanup();
43
+ resolve(value.trim());
44
+ return;
45
+ }
46
+ if (key.name === "c" && key.ctrl) {
47
+ cleanup();
48
+ process.exit(130);
49
+ }
50
+ if (key.name === "backspace") {
51
+ if (value.length > 0) {
52
+ value = value.slice(0, -1);
53
+ stdout.write("\b \b");
54
+ }
55
+ return;
56
+ }
57
+ if (key.name === "escape") {
58
+ cleanup();
59
+ resolve("");
60
+ return;
61
+ }
62
+ const ch = key.sequence;
63
+ if (!ch || key.ctrl || key.meta)
64
+ return;
65
+ if (hintVisible)
66
+ clearHint();
67
+ value += ch;
68
+ stdout.write(ch);
69
+ };
70
+ stdin.on("keypress", onKeypress);
71
+ });
72
+ }
73
+ export function formatInputPrompt() {
74
+ return ` ${c.orange}${c.bold}›${c.reset} `;
75
+ }
@@ -0,0 +1,15 @@
1
+ import { type EkzLocale } from "../locale.js";
2
+ import { type EkzMode } from "../mode.js";
3
+ export type SplashOptions = {
4
+ cwd?: string;
5
+ model?: string;
6
+ locale: EkzLocale;
7
+ mode?: EkzMode;
8
+ resumed?: boolean;
9
+ agentId?: string;
10
+ version?: string;
11
+ };
12
+ export declare function renderSplash(opts: SplashOptions): void;
13
+ export declare function renderPromptFooter(locale: EkzLocale): void;
14
+ export declare function inputPlaceholder(locale: EkzLocale, index?: number): string;
15
+ //# sourceMappingURL=splash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splash.d.ts","sourceRoot":"","sources":["../../../src/lib/ui/splash.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,cAAc,CAAC;AACzD,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAMvD,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA2GF,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAQtD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAG1D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,SAAI,GAAG,MAAM,CAIrE"}
@@ -0,0 +1,121 @@
1
+ import { userInfo } from "os";
2
+ import { basename } from "path";
3
+ import { readFileSync } from "fs";
4
+ import { dirname, join } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { formatSplashTool, splashHelpHint, splashSectionTitle, SPLASH_TOOLS, splashSessionLines, welcomeMessage, } from "../commands-i18n.js";
7
+ import { uiStrings } from "../locale.js";
8
+ import { MODE_LABELS } from "../mode.js";
9
+ import { detectShellEnvironment, shellLabel } from "../shell.js";
10
+ import { c, muted } from "../theme.js";
11
+ import { padEndVisible, truncateVisible, visibleLength } from "./layout.js";
12
+ import { renderLogoLines } from "./logo.js";
13
+ const BOX_WIDTH = 78;
14
+ const LEFT_WIDTH = 34;
15
+ const RIGHT_WIDTH = BOX_WIDTH - LEFT_WIDTH - 5;
16
+ function packageVersion() {
17
+ try {
18
+ const here = dirname(fileURLToPath(import.meta.url));
19
+ const pkg = JSON.parse(readFileSync(join(here, "../../../package.json"), "utf8"));
20
+ return pkg.version ?? "0.2.0";
21
+ }
22
+ catch {
23
+ return "0.2.0";
24
+ }
25
+ }
26
+ function displayName() {
27
+ const fromEnv = process.env.EKZ_USER_NAME?.trim();
28
+ if (fromEnv)
29
+ return fromEnv.split(/\s+/)[0] ?? fromEnv;
30
+ try {
31
+ const u = userInfo().username.replace(/[._-].*$/, "");
32
+ if (u)
33
+ return u.charAt(0).toUpperCase() + u.slice(1);
34
+ }
35
+ catch {
36
+ /* ignore */
37
+ }
38
+ return "Alberto";
39
+ }
40
+ function userLabel() {
41
+ const email = process.env.EKZ_USER_EMAIL?.trim();
42
+ if (email)
43
+ return email;
44
+ try {
45
+ return userInfo().username;
46
+ }
47
+ catch {
48
+ return "developer";
49
+ }
50
+ }
51
+ function gold(text) {
52
+ return `${c.bold}\x1b[38;5;220m${text}${c.reset}`;
53
+ }
54
+ function buildLeftColumn(opts, locale) {
55
+ const name = displayName();
56
+ const project = opts.cwd ? `~/${basename(opts.cwd)}` : "~";
57
+ const modelShort = (opts.model ?? "composer-2.5 (fast)").replace(/\s*\(fast\)/i, "").trim();
58
+ const lang = locale === "pt" ? "Português" : locale === "zh" ? "中文" : "English";
59
+ const user = truncateVisible(userLabel(), 16);
60
+ const modeLabel = opts.mode ? MODE_LABELS[opts.mode][locale] : MODE_LABELS.agent[locale];
61
+ const top = [];
62
+ top.push(gold(welcomeMessage(locale, name, Boolean(opts.resumed))));
63
+ top.push("");
64
+ for (const row of renderLogoLines()) {
65
+ top.push(truncateVisible(row, LEFT_WIDTH));
66
+ }
67
+ const shell = shellLabel(detectShellEnvironment());
68
+ const footer = [
69
+ muted(`${modelShort} · ${lang} · ${user}`),
70
+ muted(truncateVisible(`${modeLabel} · ${shell}`, LEFT_WIDTH)),
71
+ muted(project),
72
+ ];
73
+ return { top, footer };
74
+ }
75
+ function buildRightColumn(locale) {
76
+ const lines = [];
77
+ lines.push(gold(splashSectionTitle(locale, "tools")));
78
+ for (const tool of SPLASH_TOOLS[locale]) {
79
+ lines.push(muted(` ${formatSplashTool(locale, tool)}`));
80
+ }
81
+ lines.push("");
82
+ lines.push(muted("─".repeat(26)));
83
+ lines.push("");
84
+ lines.push(gold(splashSectionTitle(locale, "sessions")));
85
+ for (const hint of splashSessionLines(locale))
86
+ lines.push(muted(` ${hint}`));
87
+ return lines;
88
+ }
89
+ function renderTwoColumnBox(title, leftTop, leftFooter, right) {
90
+ const inner = BOX_WIDTH - 2;
91
+ const border = `${c.orange}${c.bold}`;
92
+ const divider = `${border}│${c.reset}`;
93
+ const rows = Math.max(leftTop.length + leftFooter.length, right.length);
94
+ const pad = Math.max(0, rows - leftTop.length - leftFooter.length);
95
+ const left = [...leftTop, ...Array(pad).fill(""), ...leftFooter];
96
+ const dashRight = Math.max(0, inner - visibleLength(title) - 6);
97
+ console.log(` ${border}╭───${title}${"─".repeat(dashRight)}╮${c.reset}`);
98
+ for (let i = 0; i < rows; i++) {
99
+ const l = padEndVisible(left[i] ?? "", LEFT_WIDTH);
100
+ const r = padEndVisible(truncateVisible(right[i] ?? "", RIGHT_WIDTH), RIGHT_WIDTH);
101
+ console.log(` ${border}│${c.reset} ${l} ${divider} ${r} ${border}│${c.reset}`);
102
+ }
103
+ console.log(` ${border}╰${"─".repeat(inner)}╯${c.reset}`);
104
+ }
105
+ export function renderSplash(opts) {
106
+ const version = opts.version ?? packageVersion();
107
+ const title = ` By Alberto Moisés v${version} `;
108
+ const left = buildLeftColumn(opts, opts.locale);
109
+ console.log("");
110
+ renderTwoColumnBox(title, left.top, left.footer, buildRightColumn(opts.locale));
111
+ console.log("");
112
+ }
113
+ export function renderPromptFooter(locale) {
114
+ console.log(` ${muted(splashHelpHint(locale))}`);
115
+ console.log("");
116
+ }
117
+ export function inputPlaceholder(locale, index = 0) {
118
+ const t = uiStrings(locale);
119
+ const pool = t.inputPlaceholders ?? [t.inputPlaceholder];
120
+ return pool[index % pool.length] ?? 'Try "run ekz doctor"';
121
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@ekzs/cli",
3
+ "version": "0.2.0",
4
+ "description": "CLI agent for e-Kwanza v2.4 — health checks, code scan, webhook tests, AI assistance",
5
+ "type": "module",
6
+ "bin": {
7
+ "ekz": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": ["dist", "skills", "README.md"],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "build:local": "npm run build && node scripts/sync-skills.mjs",
15
+ "sync:skills": "node scripts/sync-skills.mjs",
16
+ "dev": "tsc --watch",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "dependencies": {
20
+ "@cursor/sdk": "^1.0.13",
21
+ "@ekzs/connect": "^0.1.0",
22
+ "commander": "^12.1.0",
23
+ "dotenv": "^16.4.7"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20",
27
+ "typescript": "^5"
28
+ },
29
+ "keywords": ["ekwanza", "ekzs", "cli", "payments", "angola", "cursor", "agent"],
30
+ "author": "Alberto Moisés",
31
+ "license": "UNLICENSED",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/Almpro3/ekz.git",
35
+ "directory": "packages/cli"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/Almpro3/ekz/issues"
39
+ },
40
+ "homepage": "https://github.com/Almpro3/ekz/tree/main/packages/cli#readme",
41
+ "engines": {
42
+ "node": ">=18"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "registry": "https://registry.npmjs.org/"
47
+ }
48
+ }
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: ekz-connect
3
+ description: >-
4
+ e-Kwanza payment architecture orchestrator. Routes work across payment core,
5
+ provider adapter, one-time products, ticket/invite selling, subscriptions,
6
+ overage, webhook normalization, and integration playbook.
7
+ ---
8
+
9
+ # Ekz Connect - orchestrator
10
+
11
+ You are **Ekz**, a local coding agent for e-Kwanza-powered payment systems.
12
+
13
+ ## Core principle
14
+
15
+ e-Kwanza is a one-time payment rail. Reliable products, invite/ticket sales, subscriptions, and overage billing come from a clean local payment architecture built on top of that rail.
16
+
17
+ Internal reference implementations are learning material only. Do not copy their plans, tables, routes, or UI blindly. Extract the pattern and map it into the target codebase.
18
+
19
+ ## Posture
20
+
21
+ - Scope: payment architecture and e-Kwanza integration.
22
+ - Read unrelated code for context; edit only the payment surface needed for the task.
23
+ - Prefer the target app's existing models, jobs, auth, and route patterns.
24
+ - Use `@ekzs/connect` for provider-specific e-Kwanza work when possible.
25
+ - Never log or commit secrets.
26
+ - Keep business fulfillment separate from provider callback handling.
27
+ - Do not mention internal reference app names to end users unless they ask about them.
28
+
29
+ ## Shell commands (Windows / PowerShell)
30
+
31
+ The local agent runs shell tool commands in the user's terminal. On Windows the default is often **PowerShell**, not bash.
32
+
33
+ - **Never** use bare `curl` on Windows — it is an alias for `Invoke-WebRequest` and will prompt for `Uri:`. Use `curl.exe -s "https://..."` with a quoted URL, `Invoke-WebRequest -Uri "..." -UseBasicParsing`, or Node `fetch`.
34
+ - Avoid bash-only syntax (`export VAR=`, `| head`, `/dev/null`, `$(...)`) unless you explicitly run `bash -lc "..."`.
35
+ - Prefer Ekz CLI helpers (load `.env.local` automatically): `ekz doctor`, `ekz health`, `ekz scan`, `ekz webhook <url>`.
36
+ - Ekz CLI detects the terminal at launch (Cursor settings, Git Bash, or Windows PowerShell default) and passes shell rules to the agent automatically — no manual flag needed.
37
+ - Quote paths and URLs that contain spaces.
38
+
39
+ ## User intent expansion
40
+
41
+ Users should be able to ask in plain product language. Expand simple prompts into the right architecture internally:
42
+
43
+ | User says | Interpret as |
44
+ |-----------|--------------|
45
+ | "Create a payment link for this product" | One-time product payment with public checkout link |
46
+ | "25 products in stock" | Inventory/capacity guard plus paid-only stock decrement |
47
+ | "Integrate payment in my store" | Store checkout over one-time product payment architecture |
48
+ | "Multicaixa Express" | e-Kwanza `gpo` rail |
49
+ | "reference code" / "bank reference" | e-Kwanza `emis_ref` rail unless the codebase already uses Ticket API for this wording |
50
+ | "SMS/QR/e-Kwanza wallet code" | e-Kwanza `ticket` rail |
51
+ | "sell tickets/invites" | Ticket/invite business flow with reservation and issuance |
52
+ | "subscriptions" / "monthly billing" | Local recurring billing cycles over one-time payment requests |
53
+ | "charge extra usage" / "overage" | Metered usage ledger plus one-time overage charge |
54
+
55
+ The final user response should talk about the feature they asked for, not the internal skill names or reference apps.
56
+
57
+ ## Workflow
58
+
59
+ 1. Classify the task by architecture.
60
+ 2. Inspect existing account, order, ticket, subscription, usage, payment, webhook, and job models.
61
+ 3. Load the relevant skills.
62
+ 4. Map generic payment primitives into the app's vocabulary.
63
+ 5. Put e-Kwanza behind a provider adapter.
64
+ 6. Normalize webhooks before running domain handlers.
65
+ 7. Add focused tests for state transitions and duplicate callbacks.
66
+
67
+ ## Route by architecture
68
+
69
+ | Problem | Skill |
70
+ |---------|-------|
71
+ | Reusable primitives, state machines, ledger, provider boundary | **ekz-payment-core-architecture** |
72
+ | e-Kwanza auth, config, GPO, EMIS ref, Ticket API, env validation | **ekz-ekwanza-provider-adapter** |
73
+ | Product/order/invoice checkout, paid fulfillment, receipts | **ekz-one-time-product-payments** |
74
+ | Invite/ticket inventory, reservations, QR/code issuance | **ekz-ticket-invite-selling** |
75
+ | Recurring billing, trials, grace, dunning, entitlement extension | **ekz-subscription-billing** |
76
+ | Metered usage, usage ledger, overage charges, settlement | **ekz-overage-billing** |
77
+ | Callback parsing, idempotency, status mapping, routing | **ekz-webhook-normalization** |
78
+ | Unknown codebase adaptation and implementation order | **ekz-integration-playbook** |
79
+ | SDK API, CLI commands, `ekz doctor`, `ekz scan` | **ekz-sdk-cli** |
80
+
81
+ ## Do not conflate
82
+
83
+ | Concept | Owner |
84
+ |---------|-------|
85
+ | `PaymentRequest`, `PaymentAttempt`, ledger, idempotency | Payment Core |
86
+ | GPO, EMIS ref, Ticket API, HMAC | e-Kwanza Provider Adapter |
87
+ | Product sale or invoice payment | One-Time Product Payments |
88
+ | Event ticket/invite business object | Ticket / Invite Selling |
89
+ | Subscription renewal | Subscription Billing |
90
+ | Usage above allowance | Overage Billing |
91
+ | Raw provider callback | Webhook Normalization |
92
+ | Mapping into an existing app | Integration Playbook |
93
+
94
+ The e-Kwanza `ticket` rail is not the same thing as selling event tickets or invites.
95
+
96
+ ## Internal references
97
+
98
+ - **Ekz Connect** (`c:\Users\alber\ekz`): provider adapter, checkout links, payment collections, stock-like fulfillment.
99
+ - `eloapp/frontend/src/lib/billing/ekwanza-recurring.ts`: recurring one-time billing requests for subscriptions.