kodingo-cli 1.1.10 → 1.1.12
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/dist/cli.js +14 -0
- package/dist/commands/build.js +178 -0
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ const sync_graph_1 = require("./commands/sync-graph");
|
|
|
18
18
|
const sync_dependencies_1 = require("./commands/sync-dependencies");
|
|
19
19
|
const security_scan_1 = require("./commands/security-scan");
|
|
20
20
|
const init_1 = require("./commands/init");
|
|
21
|
+
const build_1 = require("./commands/build");
|
|
21
22
|
const install_hook_1 = require("./commands/install-hook");
|
|
22
23
|
const update_1 = require("./commands/update");
|
|
23
24
|
const sync_1 = require("./commands/sync");
|
|
@@ -31,6 +32,19 @@ const { version } = require("../package.json");
|
|
|
31
32
|
program.name("kodingo").description("Kodingo CLI").version(version);
|
|
32
33
|
// ── init ──────────────────────────────────────────────────────────────────────
|
|
33
34
|
(0, init_1.registerInitCommand)(program);
|
|
35
|
+
// ── build ─────────────────────────────────────────────────────────────────────
|
|
36
|
+
program
|
|
37
|
+
.command("build")
|
|
38
|
+
.description("Run Kortex Build's local agent loop against this repo (test-driver for kodingo-agent)")
|
|
39
|
+
.argument("<task>", "what to build or fix")
|
|
40
|
+
.option("--repo <path>", "repo path (defaults to the current repo)")
|
|
41
|
+
.option("--max-turns <n>", "override the agent loop's turn budget")
|
|
42
|
+
.action(async (task, options) => {
|
|
43
|
+
await (0, build_1.buildCommand)(String(task), {
|
|
44
|
+
...(options.repo ? { repo: String(options.repo) } : {}),
|
|
45
|
+
...(options.maxTurns ? { maxTurns: Number.parseInt(String(options.maxTurns), 10) } : {}),
|
|
46
|
+
});
|
|
47
|
+
});
|
|
34
48
|
// ── update ────────────────────────────────────────────────────────────────────
|
|
35
49
|
program
|
|
36
50
|
.command("update")
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* kodingo build
|
|
4
|
+
*
|
|
5
|
+
* Runs Kortex Build's real local agent loop (kodingo-agent) against the
|
|
6
|
+
* current repo. Per this repo's own stated identity — "a test driver and
|
|
7
|
+
* plumbing layer, not the final product UX" — this command is deliberately
|
|
8
|
+
* minimal: it exists to exercise kodingo-agent headlessly against a real
|
|
9
|
+
* repo, not to be a polished second Build UX (that's kodingo-vscode's job).
|
|
10
|
+
*
|
|
11
|
+
* The loop runs entirely in this process, on this machine's real files and
|
|
12
|
+
* shell. Every model decision is one real HTTP call to kodingo-api's
|
|
13
|
+
* POST /build/:id/agent-turn — this command never calls a model directly.
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.buildCommand = buildCommand;
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const readline = __importStar(require("node:readline"));
|
|
53
|
+
const kodingo_agent_1 = require("kodingo-agent");
|
|
54
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
55
|
+
function findRepoRoot(startPath) {
|
|
56
|
+
let current = startPath;
|
|
57
|
+
while (true) {
|
|
58
|
+
if (fs.existsSync(path.join(current, ".git")))
|
|
59
|
+
return current;
|
|
60
|
+
const parent = path.dirname(current);
|
|
61
|
+
if (parent === current)
|
|
62
|
+
return startPath;
|
|
63
|
+
current = parent;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function prompt(rl, question) {
|
|
67
|
+
return new Promise((resolve) => rl.question(question, (ans) => resolve(ans.trim())));
|
|
68
|
+
}
|
|
69
|
+
async function confirmTrust(repoRoot) {
|
|
70
|
+
if ((0, kodingo_agent_1.isProjectTrusted)(repoRoot)) {
|
|
71
|
+
console.log(`✔ ${repoRoot} is already trusted for Kortex Build.\n`);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
75
|
+
try {
|
|
76
|
+
console.log(`\nKortex Build will read, edit, and run allowed commands in:\n ${repoRoot}\n`);
|
|
77
|
+
const answer = (await prompt(rl, "Allow? [o]nce / [a]lways / [c]ancel: ")).toLowerCase();
|
|
78
|
+
if (answer.startsWith("a")) {
|
|
79
|
+
(0, kodingo_agent_1.trustProject)(repoRoot);
|
|
80
|
+
console.log("✔ Always allowed for this repo.\n");
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (answer.startsWith("o"))
|
|
84
|
+
return true;
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
rl.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// writeWorkspaceConfig (kodingo init) stores workspace entries keyed by the
|
|
92
|
+
// raw repo root path, but getPersistence() reads them back through a
|
|
93
|
+
// lowercased normalizePath() — a pre-existing mismatch on any path with
|
|
94
|
+
// uppercase characters (e.g. /Users/... on macOS), unrelated to this
|
|
95
|
+
// command. Checking both the raw key and the global fallback here gives
|
|
96
|
+
// this command the best real chance of finding a token either way, without
|
|
97
|
+
// trying to fix that mismatch itself.
|
|
98
|
+
function resolveApiConfig(repoRoot) {
|
|
99
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
100
|
+
const ws = (0, persistence_config_1.readWorkspaceConfig)(repoRoot);
|
|
101
|
+
const apiUrl = ws?.apiUrl ?? config.apiUrl;
|
|
102
|
+
const token = ws?.token ?? config.token;
|
|
103
|
+
if (!apiUrl || !token)
|
|
104
|
+
return null;
|
|
105
|
+
return { apiUrl, token };
|
|
106
|
+
}
|
|
107
|
+
async function buildCommand(input, options = {}) {
|
|
108
|
+
const repoRoot = options.repo ? path.resolve(options.repo) : findRepoRoot(process.cwd());
|
|
109
|
+
const apiConfig = resolveApiConfig(repoRoot);
|
|
110
|
+
if (!apiConfig) {
|
|
111
|
+
console.error(`✖ No Kodingo project connected for ${repoRoot}. Run \`kodingo init\` in this repo first.`);
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const allowed = await confirmTrust(repoRoot);
|
|
116
|
+
if (!allowed) {
|
|
117
|
+
console.log("✖ Cancelled.");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const apiBaseUrl = apiConfig.apiUrl.replace(/\/$/, "");
|
|
121
|
+
// Bring-your-own, if configured (~/.kodingo/byo-model.json — same file
|
|
122
|
+
// kodingo-vscode already uses) — never logged, only forwarded.
|
|
123
|
+
const byoModelConfig = (0, kodingo_agent_1.getByoModelConfig)();
|
|
124
|
+
if (byoModelConfig) {
|
|
125
|
+
console.log(`Using bring-your-own model${byoModelConfig.label ? ` (${byoModelConfig.label})` : ""} — no shared quota consumed.`);
|
|
126
|
+
}
|
|
127
|
+
console.log(`Starting Kortex Build against ${repoRoot} ...`);
|
|
128
|
+
const startRes = await fetch(`${apiBaseUrl}/build/agent-start`, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: { "Content-Type": "application/json", "X-Kodingo-Token": apiConfig.token },
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
input,
|
|
133
|
+
repo: path.basename(repoRoot),
|
|
134
|
+
...(byoModelConfig ? { modelConfig: byoModelConfig } : {}),
|
|
135
|
+
}),
|
|
136
|
+
});
|
|
137
|
+
const startBody = (await startRes.json());
|
|
138
|
+
if (!startRes.ok || !startBody.sessionId) {
|
|
139
|
+
console.error(`✖ Could not start Build session: ${startBody.error ?? startRes.status}`);
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const sessionId = startBody.sessionId;
|
|
144
|
+
console.log(`Session: ${sessionId}`);
|
|
145
|
+
const modelPort = (0, kodingo_agent_1.createHttpModelCallPort)({ apiBaseUrl, projectToken: apiConfig.token, sessionId });
|
|
146
|
+
const initialMessages = [
|
|
147
|
+
{
|
|
148
|
+
role: "system",
|
|
149
|
+
content: "You are Kortex Build, a coding agent working directly on the developer's real local repository. " +
|
|
150
|
+
"Explore before you edit: read files, grep for symbols, and look around as needed — looking is just " +
|
|
151
|
+
"another turn, you can call tools as many times as it takes to actually understand the real code " +
|
|
152
|
+
"before changing it. Only use edit_file for surgical, minimal changes to existing files; use " +
|
|
153
|
+
"write_file only for brand-new files. Before reporting done, verify your change with run_command.",
|
|
154
|
+
},
|
|
155
|
+
{ role: "user", content: input },
|
|
156
|
+
];
|
|
157
|
+
const result = await (0, kodingo_agent_1.runAgentLoop)({
|
|
158
|
+
cwd: repoRoot,
|
|
159
|
+
tools: (0, kodingo_agent_1.createDefaultToolSet)(),
|
|
160
|
+
initialMessages,
|
|
161
|
+
modelPort,
|
|
162
|
+
reporter: (0, kodingo_agent_1.createTerminalReporter)(),
|
|
163
|
+
...(options.maxTurns ? { budgets: { maxTurns: options.maxTurns } } : {}),
|
|
164
|
+
});
|
|
165
|
+
try {
|
|
166
|
+
await fetch(`${apiBaseUrl}/build/${sessionId}/agent-complete`, {
|
|
167
|
+
method: "POST",
|
|
168
|
+
headers: { "Content-Type": "application/json", "X-Kodingo-Token": apiConfig.token },
|
|
169
|
+
body: JSON.stringify({ status: result.status, message: result.error }),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Best-effort — the loop's own outcome already printed to the terminal regardless.
|
|
174
|
+
}
|
|
175
|
+
if (result.status !== "completed") {
|
|
176
|
+
process.exitCode = 1;
|
|
177
|
+
}
|
|
178
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kodingo-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
4
4
|
"description": "Kodingo CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@typescript-eslint/types": "^8.61.1",
|
|
26
26
|
"@typescript-eslint/typescript-estree": "^8.61.1",
|
|
27
27
|
"commander": "^12.1.0",
|
|
28
|
+
"kodingo-agent": "^0.1.1",
|
|
28
29
|
"kodingo-core": "^0.2.1",
|
|
29
30
|
"open": "^11.0.0",
|
|
30
31
|
"simple-git": "^3.30.0",
|