@thejeetsingh/kalcode 2.1.0 → 2.2.1

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 (125) hide show
  1. package/README.md +89 -89
  2. package/dist/bin/kalcode.d.ts +2 -0
  3. package/dist/bin/kalcode.js +12 -0
  4. package/dist/bin/kalcode.js.map +1 -0
  5. package/dist/src/agent/context.d.ts +6 -0
  6. package/dist/src/agent/context.js +60 -0
  7. package/dist/src/agent/context.js.map +1 -0
  8. package/dist/src/agent/history.d.ts +8 -0
  9. package/dist/src/agent/history.js +59 -0
  10. package/dist/src/agent/history.js.map +1 -0
  11. package/dist/src/agent/loop.d.ts +6 -0
  12. package/dist/src/agent/loop.js +235 -0
  13. package/dist/src/agent/loop.js.map +1 -0
  14. package/dist/src/agent/memory.d.ts +2 -0
  15. package/dist/src/agent/memory.js +27 -0
  16. package/dist/src/agent/memory.js.map +1 -0
  17. package/dist/src/agent/permissions.d.ts +5 -0
  18. package/dist/src/agent/permissions.js +66 -0
  19. package/dist/src/agent/permissions.js.map +1 -0
  20. package/dist/src/agent/text-tool-parser.d.ts +2 -0
  21. package/dist/src/agent/text-tool-parser.js +68 -0
  22. package/dist/src/agent/text-tool-parser.js.map +1 -0
  23. package/dist/src/api/client.d.ts +2 -0
  24. package/dist/src/api/client.js +86 -0
  25. package/dist/src/api/client.js.map +1 -0
  26. package/dist/src/api/stream-parser.d.ts +2 -0
  27. package/dist/src/api/stream-parser.js +97 -0
  28. package/dist/src/api/stream-parser.js.map +1 -0
  29. package/dist/src/config.d.ts +7 -0
  30. package/dist/src/config.js +52 -0
  31. package/dist/src/config.js.map +1 -0
  32. package/dist/src/constants.d.ts +25 -0
  33. package/{src/constants.ts → dist/src/constants.js} +15 -20
  34. package/dist/src/constants.js.map +1 -0
  35. package/dist/src/git/git.d.ts +15 -0
  36. package/dist/src/git/git.js +73 -0
  37. package/dist/src/git/git.js.map +1 -0
  38. package/dist/src/index.d.ts +1 -0
  39. package/dist/src/index.js +415 -0
  40. package/dist/src/index.js.map +1 -0
  41. package/dist/src/proxy/server.d.ts +1 -0
  42. package/dist/src/proxy/server.js +92 -0
  43. package/dist/src/proxy/server.js.map +1 -0
  44. package/dist/src/tools/edit-file.d.ts +2 -0
  45. package/dist/src/tools/edit-file.js +88 -0
  46. package/dist/src/tools/edit-file.js.map +1 -0
  47. package/dist/src/tools/glob-tool.d.ts +2 -0
  48. package/dist/src/tools/glob-tool.js +52 -0
  49. package/dist/src/tools/glob-tool.js.map +1 -0
  50. package/dist/src/tools/grep.d.ts +2 -0
  51. package/dist/src/tools/grep.js +93 -0
  52. package/dist/src/tools/grep.js.map +1 -0
  53. package/dist/src/tools/list-directory.d.ts +2 -0
  54. package/dist/src/tools/list-directory.js +90 -0
  55. package/dist/src/tools/list-directory.js.map +1 -0
  56. package/dist/src/tools/read-file.d.ts +2 -0
  57. package/dist/src/tools/read-file.js +64 -0
  58. package/dist/src/tools/read-file.js.map +1 -0
  59. package/dist/src/tools/registry.d.ts +4 -0
  60. package/dist/src/tools/registry.js +32 -0
  61. package/dist/src/tools/registry.js.map +1 -0
  62. package/dist/src/tools/run-command.d.ts +2 -0
  63. package/dist/src/tools/run-command.js +98 -0
  64. package/dist/src/tools/run-command.js.map +1 -0
  65. package/dist/src/tools/write-file.d.ts +2 -0
  66. package/dist/src/tools/write-file.js +39 -0
  67. package/dist/src/tools/write-file.js.map +1 -0
  68. package/dist/src/types.d.ts +61 -0
  69. package/dist/src/types.js +2 -0
  70. package/dist/src/types.js.map +1 -0
  71. package/dist/src/ui/input.d.ts +5 -0
  72. package/dist/src/ui/input.js +52 -0
  73. package/dist/src/ui/input.js.map +1 -0
  74. package/dist/src/ui/model-picker.d.ts +7 -0
  75. package/dist/src/ui/model-picker.js +70 -0
  76. package/dist/src/ui/model-picker.js.map +1 -0
  77. package/dist/src/ui/skills-picker.d.ts +2 -0
  78. package/dist/src/ui/skills-picker.js +95 -0
  79. package/dist/src/ui/skills-picker.js.map +1 -0
  80. package/dist/src/ui/skills.d.ts +14 -0
  81. package/dist/src/ui/skills.js +137 -0
  82. package/dist/src/ui/skills.js.map +1 -0
  83. package/dist/src/ui/spinner.d.ts +5 -0
  84. package/dist/src/ui/spinner.js +49 -0
  85. package/dist/src/ui/spinner.js.map +1 -0
  86. package/dist/src/ui/stream-renderer.d.ts +2 -0
  87. package/dist/src/ui/stream-renderer.js +66 -0
  88. package/dist/src/ui/stream-renderer.js.map +1 -0
  89. package/dist/src/ui/terminal.d.ts +24 -0
  90. package/dist/src/ui/terminal.js +272 -0
  91. package/dist/src/ui/terminal.js.map +1 -0
  92. package/package.json +16 -16
  93. package/api/health.ts +0 -8
  94. package/api/v1/chat/completions.ts +0 -43
  95. package/bin/kalcode.ts +0 -14
  96. package/src/agent/context.ts +0 -62
  97. package/src/agent/history.ts +0 -70
  98. package/src/agent/loop.ts +0 -282
  99. package/src/agent/memory.ts +0 -26
  100. package/src/agent/permissions.ts +0 -84
  101. package/src/agent/text-tool-parser.ts +0 -71
  102. package/src/api/client.ts +0 -105
  103. package/src/api/stream-parser.ts +0 -109
  104. package/src/config.ts +0 -61
  105. package/src/git/git.ts +0 -86
  106. package/src/index.ts +0 -403
  107. package/src/proxy/server.ts +0 -110
  108. package/src/tools/edit-file.ts +0 -97
  109. package/src/tools/glob-tool.ts +0 -59
  110. package/src/tools/grep.ts +0 -96
  111. package/src/tools/list-directory.ts +0 -101
  112. package/src/tools/read-file.ts +0 -71
  113. package/src/tools/registry.ts +0 -41
  114. package/src/tools/run-command.ts +0 -99
  115. package/src/tools/write-file.ts +0 -42
  116. package/src/types.ts +0 -68
  117. package/src/ui/input.ts +0 -60
  118. package/src/ui/model-picker.ts +0 -92
  119. package/src/ui/skills-picker.ts +0 -113
  120. package/src/ui/skills.ts +0 -152
  121. package/src/ui/spinner.ts +0 -56
  122. package/src/ui/stream-renderer.ts +0 -69
  123. package/src/ui/terminal.ts +0 -337
  124. package/tsconfig.json +0 -16
  125. package/vercel.json +0 -13
@@ -0,0 +1,27 @@
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { join } from "path";
3
+ const MEMORY_FILES = ["KALCODE.md", "CONVENTIONS.md", ".kalcode.md"];
4
+ export function loadProjectMemory(cwd) {
5
+ for (const name of MEMORY_FILES) {
6
+ const path = join(cwd, name);
7
+ if (existsSync(path)) {
8
+ try {
9
+ const content = readFileSync(path, "utf-8").trim();
10
+ if (content)
11
+ return content;
12
+ }
13
+ catch {
14
+ // ignore
15
+ }
16
+ }
17
+ }
18
+ return null;
19
+ }
20
+ export function getMemoryFileName(cwd) {
21
+ for (const name of MEMORY_FILES) {
22
+ if (existsSync(join(cwd, name)))
23
+ return name;
24
+ }
25
+ return null;
26
+ }
27
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/agent/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,YAAY,GAAG,CAAC,YAAY,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC;AAErE,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;gBACnD,IAAI,OAAO;oBAAE,OAAO,OAAO,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type PermissionLevel = "ask" | "auto" | "deny-writes";
2
+ export declare function setPermissionLevel(level: PermissionLevel): void;
3
+ export declare function getPermissionLevel(): PermissionLevel;
4
+ export declare function needsPermission(toolName: string): boolean;
5
+ export declare function requestPermission(toolName: string, summary: string): Promise<boolean>;
@@ -0,0 +1,66 @@
1
+ import chalk from "chalk";
2
+ let permissionLevel = "ask";
3
+ const sessionApprovals = new Set();
4
+ export function setPermissionLevel(level) {
5
+ permissionLevel = level;
6
+ }
7
+ export function getPermissionLevel() {
8
+ return permissionLevel;
9
+ }
10
+ // Tools that modify state
11
+ const WRITE_TOOLS = new Set(["writeFile", "editFile", "runCommand"]);
12
+ export function needsPermission(toolName) {
13
+ if (permissionLevel === "auto")
14
+ return false;
15
+ if (permissionLevel === "deny-writes" && WRITE_TOOLS.has(toolName))
16
+ return false;
17
+ return WRITE_TOOLS.has(toolName);
18
+ }
19
+ export async function requestPermission(toolName, summary) {
20
+ const key = `${toolName}:${summary}`;
21
+ if (sessionApprovals.has(key))
22
+ return true;
23
+ // Claude Code style: ? marker with clean options
24
+ const promptText = ` ${chalk.yellow("?")} Allow ${chalk.bold.white(toolName)}` +
25
+ (summary ? ` ${chalk.dim(summary)}` : "") +
26
+ ` ${chalk.dim("(y)es / (n)o / (a)lways")} `;
27
+ process.stdout.write(promptText);
28
+ const answer = await readSingleKey();
29
+ const writeDecision = (decision, color) => {
30
+ // Redraw the line to eliminate any echoed keypress artifacts (e.g. "yyes").
31
+ process.stdout.write(`\r\x1b[2K${promptText}${color(decision)}\n`);
32
+ };
33
+ if (answer === "a") {
34
+ writeDecision("always", chalk.green);
35
+ setPermissionLevel("auto");
36
+ return true;
37
+ }
38
+ if (answer === "y" || answer === "\r" || answer === "\n") {
39
+ writeDecision("yes", chalk.green);
40
+ sessionApprovals.add(key);
41
+ return true;
42
+ }
43
+ writeDecision("no", chalk.red);
44
+ return false;
45
+ }
46
+ function readSingleKey() {
47
+ return new Promise((resolve) => {
48
+ const stdin = process.stdin;
49
+ const wasRaw = stdin.isRaw;
50
+ stdin.setRawMode(true);
51
+ stdin.resume();
52
+ function onData(data) {
53
+ stdin.removeListener("data", onData);
54
+ stdin.setRawMode(wasRaw ?? false);
55
+ const key = data.toString();
56
+ // Ctrl+C
57
+ if (key === "\x03") {
58
+ resolve("n");
59
+ return;
60
+ }
61
+ resolve(key.toLowerCase());
62
+ }
63
+ stdin.on("data", onData);
64
+ });
65
+ }
66
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../../src/agent/permissions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { ToolCallAccumulator } from "../types.js";
2
+ export declare function parseTextToolCalls(text: string): ToolCallAccumulator[] | null;
@@ -0,0 +1,68 @@
1
+ // Parse tool calls from model text output when the model doesn't use
2
+ // the proper tool_call API. Handles formats like:
3
+ // ```tool_code
4
+ // [toolName(arg1='val1', arg2='val2')]
5
+ // ```
6
+ // or JSON-like: {"name": "toolName", "arguments": {"arg": "val"}}
7
+ export function parseTextToolCalls(text) {
8
+ const results = [];
9
+ // Pattern 1: ```tool_code blocks (gemma style)
10
+ const toolCodeRegex = /```tool_code\s*\n([\s\S]*?)```/g;
11
+ let match;
12
+ while ((match = toolCodeRegex.exec(text)) !== null) {
13
+ const block = match[1].trim();
14
+ const calls = parseToolCodeBlock(block);
15
+ results.push(...calls);
16
+ }
17
+ if (results.length > 0)
18
+ return results;
19
+ // Pattern 2: [toolName(args)] without code blocks
20
+ const bracketRegex = /\[(\w+)\(([^)]*)\)\]/g;
21
+ while ((match = bracketRegex.exec(text)) !== null) {
22
+ const name = match[1];
23
+ const argsStr = match[2];
24
+ const args = parseKeyValueArgs(argsStr);
25
+ results.push({
26
+ id: `text-${Date.now()}-${results.length}`,
27
+ function: { name, arguments: JSON.stringify(args) },
28
+ });
29
+ }
30
+ return results.length > 0 ? results : null;
31
+ }
32
+ function parseToolCodeBlock(block) {
33
+ const results = [];
34
+ // Match [toolName(key='value', key2='value2')] or [toolName(key="value")]
35
+ const callRegex = /\[?(\w+)\(([^)]*)\)\]?/g;
36
+ let match;
37
+ while ((match = callRegex.exec(block)) !== null) {
38
+ const name = match[1];
39
+ const argsStr = match[2];
40
+ const args = parseKeyValueArgs(argsStr);
41
+ results.push({
42
+ id: `text-${Date.now()}-${results.length}`,
43
+ function: { name, arguments: JSON.stringify(args) },
44
+ });
45
+ }
46
+ return results;
47
+ }
48
+ function parseKeyValueArgs(argsStr) {
49
+ const args = {};
50
+ // Match key='value' or key="value" or key=value or key=true/false/number
51
+ const argRegex = /(\w+)\s*=\s*(?:'([^']*)'|"([^"]*)"|(\S+))/g;
52
+ let match;
53
+ while ((match = argRegex.exec(argsStr)) !== null) {
54
+ const key = match[1];
55
+ const value = match[2] ?? match[3] ?? match[4];
56
+ // Try to parse as boolean/number
57
+ if (value === "true")
58
+ args[key] = true;
59
+ else if (value === "false")
60
+ args[key] = false;
61
+ else if (!isNaN(Number(value)) && value !== "")
62
+ args[key] = Number(value);
63
+ else
64
+ args[key] = value;
65
+ }
66
+ return args;
67
+ }
68
+ //# sourceMappingURL=text-tool-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-tool-parser.js","sourceRoot":"","sources":["../../../src/agent/text-tool-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Message, StreamDelta, ToolDefinition } from "../types.js";
2
+ export declare function streamChat(apiKey: string, model: string, messages: Message[], tools?: ToolDefinition[], onRetry?: (attempt: number, waitSec: number) => void): AsyncGenerator<StreamDelta>;
@@ -0,0 +1,86 @@
1
+ import { API_URL, DEFAULT_PROXY_URL, RATE_LIMIT_MAX, RATE_LIMIT_WINDOW, MAX_RETRIES, RETRY_BASE_DELAY } from "../constants.js";
2
+ import { parseSSEStream } from "./stream-parser.js";
3
+ const requestTimestamps = [];
4
+ async function waitForRateLimit() {
5
+ const now = Date.now();
6
+ while (requestTimestamps.length > 0 && requestTimestamps[0] < now - RATE_LIMIT_WINDOW) {
7
+ requestTimestamps.shift();
8
+ }
9
+ if (requestTimestamps.length >= RATE_LIMIT_MAX) {
10
+ const oldestInWindow = requestTimestamps[0];
11
+ const waitMs = oldestInWindow + RATE_LIMIT_WINDOW - now + 100;
12
+ if (waitMs > 0) {
13
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
14
+ }
15
+ }
16
+ requestTimestamps.push(Date.now());
17
+ }
18
+ export async function* streamChat(apiKey, model, messages, tools, onRetry) {
19
+ const explicitProxy = (process.env.KALCODE_PROXY_URL || "").trim();
20
+ // If no API key and no explicit proxy, auto-use the public Vercel proxy
21
+ const proxyUrl = explicitProxy || (!apiKey ? DEFAULT_PROXY_URL : "");
22
+ const targetUrl = proxyUrl
23
+ ? `${proxyUrl.replace(/\/+$/, "")}/v1/chat/completions`
24
+ : API_URL;
25
+ const authToken = apiKey;
26
+ if (!proxyUrl && !authToken) {
27
+ yield {
28
+ type: "error",
29
+ error: "Missing NVIDIA API key.",
30
+ };
31
+ return;
32
+ }
33
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
34
+ await waitForRateLimit();
35
+ const body = {
36
+ model,
37
+ messages,
38
+ stream: true,
39
+ max_tokens: 16384,
40
+ temperature: 0,
41
+ };
42
+ if (tools && tools.length > 0) {
43
+ body.tools = tools;
44
+ body.tool_choice = "auto";
45
+ }
46
+ const response = await fetch(targetUrl, {
47
+ method: "POST",
48
+ headers: proxyUrl
49
+ ? {
50
+ "Content-Type": "application/json",
51
+ }
52
+ : {
53
+ "Content-Type": "application/json",
54
+ Authorization: `Bearer ${authToken}`,
55
+ },
56
+ body: JSON.stringify(body),
57
+ });
58
+ if (response.status === 429 && attempt < MAX_RETRIES) {
59
+ const retryAfter = response.headers.get("retry-after");
60
+ const waitMs = retryAfter
61
+ ? parseInt(retryAfter) * 1000
62
+ : RETRY_BASE_DELAY * (attempt + 1);
63
+ const waitSec = Math.ceil(waitMs / 1000);
64
+ onRetry?.(attempt + 1, waitSec);
65
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
66
+ continue;
67
+ }
68
+ if (!response.ok) {
69
+ const text = await response.text();
70
+ let errorMsg;
71
+ try {
72
+ const parsed = JSON.parse(text);
73
+ errorMsg = parsed.error?.message || text;
74
+ }
75
+ catch {
76
+ errorMsg = text;
77
+ }
78
+ yield { type: "error", error: `API error ${response.status}: ${errorMsg}` };
79
+ return;
80
+ }
81
+ yield* parseSSEStream(response);
82
+ return;
83
+ }
84
+ yield { type: "error", error: "Max retries exceeded (rate limited)" };
85
+ }
86
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/api/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { StreamDelta } from "../types.js";
2
+ export declare function parseSSEStream(response: Response): AsyncGenerator<StreamDelta>;
@@ -0,0 +1,97 @@
1
+ export async function* parseSSEStream(response) {
2
+ const reader = response.body?.getReader();
3
+ if (!reader) {
4
+ yield { type: "error", error: "No response body" };
5
+ return;
6
+ }
7
+ const decoder = new TextDecoder();
8
+ let buffer = "";
9
+ try {
10
+ while (true) {
11
+ const { done, value } = await reader.read();
12
+ if (done)
13
+ break;
14
+ buffer += decoder.decode(value, { stream: true });
15
+ const lines = buffer.split("\n");
16
+ buffer = lines.pop() || "";
17
+ for (const line of lines) {
18
+ const trimmed = line.trim();
19
+ if (!trimmed || trimmed.startsWith(":"))
20
+ continue;
21
+ if (trimmed.startsWith("data: ")) {
22
+ const data = trimmed.slice(6);
23
+ if (data === "[DONE]") {
24
+ yield { type: "done" };
25
+ return;
26
+ }
27
+ try {
28
+ const parsed = JSON.parse(data);
29
+ if (parsed.error) {
30
+ yield {
31
+ type: "error",
32
+ error: typeof parsed.error === "string"
33
+ ? parsed.error
34
+ : parsed.error.message || JSON.stringify(parsed.error),
35
+ };
36
+ continue;
37
+ }
38
+ const choice = parsed.choices?.[0];
39
+ if (!choice) {
40
+ // Check for usage in non-choice chunks
41
+ if (parsed.usage) {
42
+ yield {
43
+ type: "done",
44
+ usage: {
45
+ prompt_tokens: parsed.usage.prompt_tokens || 0,
46
+ completion_tokens: parsed.usage.completion_tokens || 0,
47
+ total_tokens: parsed.usage.total_tokens || 0,
48
+ },
49
+ };
50
+ }
51
+ continue;
52
+ }
53
+ const delta = choice.delta;
54
+ if (!delta)
55
+ continue;
56
+ // Build the base delta
57
+ if (delta.content) {
58
+ yield { type: "content", content: delta.content };
59
+ }
60
+ if (delta.tool_calls) {
61
+ for (const tc of delta.tool_calls) {
62
+ yield {
63
+ type: "tool_call",
64
+ toolCall: {
65
+ index: tc.index,
66
+ id: tc.id,
67
+ function: tc.function,
68
+ },
69
+ };
70
+ }
71
+ }
72
+ // Extract usage if present
73
+ if (parsed.usage) {
74
+ yield {
75
+ type: "content",
76
+ content: "",
77
+ usage: {
78
+ prompt_tokens: parsed.usage.prompt_tokens || 0,
79
+ completion_tokens: parsed.usage.completion_tokens || 0,
80
+ total_tokens: parsed.usage.total_tokens || 0,
81
+ },
82
+ };
83
+ }
84
+ }
85
+ catch {
86
+ // skip malformed JSON chunks
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ finally {
93
+ reader.releaseLock();
94
+ }
95
+ yield { type: "done" };
96
+ }
97
+ //# sourceMappingURL=stream-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-parser.js","sourceRoot":"","sources":["../../../src/api/stream-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { Config } from "./types.js";
2
+ export declare function loadConfig(): Config;
3
+ export declare function saveConfig(config: Config): void;
4
+ export declare function getApiKey(): string;
5
+ export declare function setApiKey(key: string): void;
6
+ export declare function getModel(): string;
7
+ export declare function setModel(model: string): void;
@@ -0,0 +1,52 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ import { DEFAULT_MODEL, AVAILABLE_MODELS } from "./constants.js";
5
+ const CONFIG_DIR = join(homedir(), ".kalcode");
6
+ const CONFIG_FILE = join(CONFIG_DIR, "config.json");
7
+ const KNOWN_MODEL_IDS = new Set(AVAILABLE_MODELS.map(m => m.id));
8
+ export function loadConfig() {
9
+ if (!existsSync(CONFIG_FILE)) {
10
+ return { apiKey: process.env.NVIDIA_NIM_KEY || "", model: DEFAULT_MODEL };
11
+ }
12
+ try {
13
+ const raw = readFileSync(CONFIG_FILE, "utf-8");
14
+ const parsed = JSON.parse(raw);
15
+ let model = parsed.model || DEFAULT_MODEL;
16
+ // Migrate old OpenRouter model IDs
17
+ if (model.includes(":free") || model.includes("openrouter/")) {
18
+ model = DEFAULT_MODEL;
19
+ }
20
+ // Ignore old OpenRouter keys
21
+ const apiKey = (parsed.apiKey && !parsed.apiKey.startsWith("sk-or-"))
22
+ ? parsed.apiKey
23
+ : (process.env.NVIDIA_NIM_KEY || "");
24
+ return { apiKey, model };
25
+ }
26
+ catch {
27
+ return { apiKey: "", model: DEFAULT_MODEL };
28
+ }
29
+ }
30
+ export function saveConfig(config) {
31
+ if (!existsSync(CONFIG_DIR)) {
32
+ mkdirSync(CONFIG_DIR, { recursive: true });
33
+ }
34
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
35
+ }
36
+ export function getApiKey() {
37
+ return loadConfig().apiKey;
38
+ }
39
+ export function setApiKey(key) {
40
+ const config = loadConfig();
41
+ config.apiKey = key;
42
+ saveConfig(config);
43
+ }
44
+ export function getModel() {
45
+ return loadConfig().model;
46
+ }
47
+ export function setModel(model) {
48
+ const config = loadConfig();
49
+ config.model = model;
50
+ saveConfig(config);
51
+ }
52
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEjE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEpD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEjE,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IAC5E,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,aAAa,CAAC;QAE1C,mCAAmC;QACnC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YAC7D,KAAK,GAAG,aAAa,CAAC;QACxB,CAAC;QAED,6BAA6B;QAC7B,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACnE,CAAC,CAAC,MAAM,CAAC,MAAM;YACf,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;QACvC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IACD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,UAAU,EAAE,CAAC,MAAM,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC;IACpB,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,UAAU,EAAE,CAAC,KAAK,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,UAAU,CAAC,MAAM,CAAC,CAAC;AACrB,CAAC"}
@@ -0,0 +1,25 @@
1
+ export declare const VERSION = "2.2.1";
2
+ export declare const API_URL = "https://integrate.api.nvidia.com/v1/chat/completions";
3
+ /** Public proxy — used automatically when no API key is set */
4
+ export declare const DEFAULT_PROXY_URL = "https://kalcode.vercel.app";
5
+ export declare const DEFAULT_MODEL = "qwen/qwen3-coder-480b-a35b-instruct";
6
+ export declare const AVAILABLE_MODELS: {
7
+ id: string;
8
+ name: string;
9
+ params: string;
10
+ }[];
11
+ export declare const MAX_LOOP_ITERATIONS = 50;
12
+ export declare const MAX_OUTPUT_BYTES: number;
13
+ export declare const DEFAULT_COMMAND_TIMEOUT = 30000;
14
+ export declare const MAX_COMMAND_TIMEOUT = 45000;
15
+ export declare const MAX_FILE_SIZE: number;
16
+ export declare const DEFAULT_LINE_LIMIT = 2000;
17
+ export declare const MAX_GREP_RESULTS = 50;
18
+ export declare const MAX_GLOB_RESULTS = 1000;
19
+ export declare const MAX_DIR_ENTRIES = 500;
20
+ export declare const MAX_DIR_DEPTH = 3;
21
+ export declare const RATE_LIMIT_WINDOW = 60000;
22
+ export declare const RATE_LIMIT_MAX = 60;
23
+ export declare const MAX_RETRIES = 3;
24
+ export declare const RETRY_BASE_DELAY = 5000;
25
+ export declare function buildSystemPrompt(cwd: string): string;
@@ -1,25 +1,20 @@
1
- export const VERSION = "2.1.0";
2
-
1
+ export const VERSION = "2.2.1";
3
2
  export const API_URL = "https://integrate.api.nvidia.com/v1/chat/completions";
4
-
5
3
  /** Public proxy — used automatically when no API key is set */
6
4
  export const DEFAULT_PROXY_URL = "https://kalcode.vercel.app";
7
-
8
5
  export const DEFAULT_MODEL = "qwen/qwen3-coder-480b-a35b-instruct";
9
-
10
- export const AVAILABLE_MODELS: { id: string; name: string; params: string }[] = [
11
- { id: "qwen/qwen3-coder-480b-a35b-instruct", name: "Qwen3 Coder 480B", params: "480B MoE" },
12
- { id: "mistralai/devstral-2-123b-instruct-2512", name: "Devstral 2 123B", params: "123B" },
13
- { id: "deepseek-ai/deepseek-v3.2", name: "DeepSeek V3.2", params: "685B" },
14
- { id: "mistralai/mistral-large-3-675b-instruct-2512", name: "Mistral Large 3", params: "675B" },
15
- { id: "qwen/qwen3.5-397b-a17b", name: "Qwen 3.5 397B", params: "397B MoE" },
16
- { id: "moonshotai/kimi-k2-instruct", name: "Kimi K2", params: "1T MoE" },
17
- { id: "z-ai/glm5", name: "GLM 5", params: "744B MoE" },
18
- { id: "nvidia/llama-3.1-nemotron-ultra-253b-v1", name: "Nemotron Ultra", params: "253B" },
19
- { id: "meta/llama-3.1-405b-instruct", name: "Llama 3.1 405B", params: "405B" },
20
- { id: "minimaxai/minimax-m2.5", name: "MiniMax M2.5", params: "230B" },
6
+ export const AVAILABLE_MODELS = [
7
+ { id: "qwen/qwen3-coder-480b-a35b-instruct", name: "Qwen3 Coder 480B", params: "480B MoE" },
8
+ { id: "mistralai/devstral-2-123b-instruct-2512", name: "Devstral 2 123B", params: "123B" },
9
+ { id: "deepseek-ai/deepseek-v3.2", name: "DeepSeek V3.2", params: "685B" },
10
+ { id: "mistralai/mistral-large-3-675b-instruct-2512", name: "Mistral Large 3", params: "675B" },
11
+ { id: "qwen/qwen3.5-397b-a17b", name: "Qwen 3.5 397B", params: "397B MoE" },
12
+ { id: "moonshotai/kimi-k2-instruct", name: "Kimi K2", params: "1T MoE" },
13
+ { id: "z-ai/glm5", name: "GLM 5", params: "744B MoE" },
14
+ { id: "nvidia/llama-3.1-nemotron-ultra-253b-v1", name: "Nemotron Ultra", params: "253B" },
15
+ { id: "meta/llama-3.1-405b-instruct", name: "Llama 3.1 405B", params: "405B" },
16
+ { id: "minimaxai/minimax-m2.5", name: "MiniMax M2.5", params: "230B" },
21
17
  ];
22
-
23
18
  export const MAX_LOOP_ITERATIONS = 50;
24
19
  export const MAX_OUTPUT_BYTES = 50 * 1024;
25
20
  export const DEFAULT_COMMAND_TIMEOUT = 30_000;
@@ -34,9 +29,8 @@ export const RATE_LIMIT_WINDOW = 60_000;
34
29
  export const RATE_LIMIT_MAX = 60;
35
30
  export const MAX_RETRIES = 3;
36
31
  export const RETRY_BASE_DELAY = 5_000;
37
-
38
- export function buildSystemPrompt(cwd: string): string {
39
- return `You are kalcode, a CLI coding agent. You help users with software engineering tasks.
32
+ export function buildSystemPrompt(cwd) {
33
+ return `You are kalcode, a CLI coding agent. You help users with software engineering tasks.
40
34
 
41
35
  IMPORTANT: Your working directory is ${cwd}
42
36
  ALL file operations MUST be relative to or inside this directory.
@@ -59,3 +53,4 @@ Rules:
59
53
  - For dangerous operations (rm, git push, etc), warn the user first.
60
54
  - Do NOT explore directories outside the working directory.`;
61
55
  }
56
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,MAAM,CAAC,MAAM,OAAO,GAAG,sDAAsD,CAAC;AAE9E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAE9D,MAAM,CAAC,MAAM,aAAa,GAAG,qCAAqC,CAAC;AAEnE,MAAM,CAAC,MAAM,gBAAgB,GAAmD;IAC9E,EAAE,EAAE,EAAE,qCAAqC,EAAE,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,UAAU,EAAE;IAC3F,EAAE,EAAE,EAAE,yCAAyC,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE;IAC1F,EAAE,EAAE,EAAE,2BAA2B,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE;IAC1E,EAAE,EAAE,EAAE,8CAA8C,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE;IAC/F,EAAE,EAAE,EAAE,wBAAwB,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE;IAC3E,EAAE,EAAE,EAAE,6BAA6B,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE;IACxE,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE;IACtD,EAAE,EAAE,EAAE,yCAAyC,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE;IACzF,EAAE,EAAE,EAAE,8BAA8B,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,EAAE;IAC9E,EAAE,EAAE,EAAE,wBAAwB,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE;CACvE,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC1C,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAC9C,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAC1C,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AACvC,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AACnC,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AACnC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAC/B,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAC7B,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAEtC,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO;;uCAE8B,GAAG;;0CAEA,GAAG;;;;;;;;;;;;;;mEAcsB,GAAG;;;4DAGV,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,15 @@
1
+ export declare function isGitRepo(cwd?: string): boolean;
2
+ export declare function gitStatus(): Promise<string>;
3
+ export declare function gitDiff(staged?: boolean): Promise<string>;
4
+ export declare function gitDiffSummary(): Promise<string>;
5
+ export declare function gitLog(count?: number): Promise<string>;
6
+ export declare function gitCommit(message: string): Promise<{
7
+ ok: boolean;
8
+ output: string;
9
+ }>;
10
+ export declare function gitUndo(): Promise<{
11
+ ok: boolean;
12
+ output: string;
13
+ }>;
14
+ export declare function gitCurrentBranch(): Promise<string>;
15
+ export declare function hasPendingChanges(): Promise<boolean>;
@@ -0,0 +1,73 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { spawn } from "child_process";
4
+ export function isGitRepo(cwd = process.cwd()) {
5
+ return existsSync(join(cwd, ".git"));
6
+ }
7
+ function run(args, cwd) {
8
+ return new Promise((resolve) => {
9
+ const proc = spawn("git", args, {
10
+ cwd: cwd || process.cwd(),
11
+ stdio: ["ignore", "pipe", "pipe"],
12
+ });
13
+ let stdout = "";
14
+ let stderr = "";
15
+ proc.stdout.on("data", (d) => { stdout += d.toString(); });
16
+ proc.stderr.on("data", (d) => { stderr += d.toString(); });
17
+ proc.on("close", (code) => {
18
+ resolve({ ok: code === 0, stdout: stdout.trim(), stderr: stderr.trim() });
19
+ });
20
+ proc.on("error", (err) => {
21
+ resolve({ ok: false, stdout: "", stderr: err.message });
22
+ });
23
+ });
24
+ }
25
+ export async function gitStatus() {
26
+ const r = await run(["status", "--short"]);
27
+ return r.ok ? (r.stdout || "Clean working tree") : `git error: ${r.stderr}`;
28
+ }
29
+ export async function gitDiff(staged = false) {
30
+ const args = staged ? ["diff", "--staged"] : ["diff"];
31
+ const r = await run(args);
32
+ return r.ok ? (r.stdout || "No changes") : `git error: ${r.stderr}`;
33
+ }
34
+ export async function gitDiffSummary() {
35
+ const r = await run(["diff", "--stat"]);
36
+ const rs = await run(["diff", "--staged", "--stat"]);
37
+ let out = "";
38
+ if (r.stdout)
39
+ out += `Unstaged:\n${r.stdout}\n`;
40
+ if (rs.stdout)
41
+ out += `Staged:\n${rs.stdout}`;
42
+ return out || "No changes";
43
+ }
44
+ export async function gitLog(count = 5) {
45
+ const r = await run(["log", `--oneline`, `-${count}`]);
46
+ return r.ok ? (r.stdout || "No commits yet") : `git error: ${r.stderr}`;
47
+ }
48
+ export async function gitCommit(message) {
49
+ await run(["add", "-A"]);
50
+ const r = await run(["commit", "-m", message]);
51
+ return { ok: r.ok, output: r.ok ? r.stdout : r.stderr };
52
+ }
53
+ export async function gitUndo() {
54
+ const log = await run(["log", "--oneline", "-1"]);
55
+ if (!log.ok || !log.stdout) {
56
+ return { ok: false, output: "No commits to undo" };
57
+ }
58
+ const r = await run(["reset", "--soft", "HEAD~1"]);
59
+ if (r.ok) {
60
+ await run(["reset", "HEAD"]);
61
+ return { ok: true, output: `Undid: ${log.stdout}` };
62
+ }
63
+ return { ok: false, output: r.stderr };
64
+ }
65
+ export async function gitCurrentBranch() {
66
+ const r = await run(["branch", "--show-current"]);
67
+ return r.ok ? r.stdout : "unknown";
68
+ }
69
+ export async function hasPendingChanges() {
70
+ const r = await run(["status", "--porcelain"]);
71
+ return r.ok && r.stdout.length > 0;
72
+ }
73
+ //# sourceMappingURL=git.js.map