kc-beta 0.1.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 (141) hide show
  1. package/bin/kc-beta.js +16 -0
  2. package/package.json +32 -0
  3. package/src/agent/confidence-scorer.js +120 -0
  4. package/src/agent/context.js +124 -0
  5. package/src/agent/corner-case-registry.js +119 -0
  6. package/src/agent/engine.js +224 -0
  7. package/src/agent/events.js +27 -0
  8. package/src/agent/history.js +101 -0
  9. package/src/agent/llm-client.js +131 -0
  10. package/src/agent/pipelines/base.js +14 -0
  11. package/src/agent/pipelines/distillation.js +113 -0
  12. package/src/agent/pipelines/extraction.js +92 -0
  13. package/src/agent/pipelines/index.js +23 -0
  14. package/src/agent/pipelines/initializer.js +163 -0
  15. package/src/agent/pipelines/production-qc.js +99 -0
  16. package/src/agent/pipelines/skill-authoring.js +83 -0
  17. package/src/agent/pipelines/skill-testing.js +111 -0
  18. package/src/agent/tools/agent-tool.js +100 -0
  19. package/src/agent/tools/base.js +35 -0
  20. package/src/agent/tools/dashboard-render.js +146 -0
  21. package/src/agent/tools/document-parse.js +184 -0
  22. package/src/agent/tools/document-search.js +111 -0
  23. package/src/agent/tools/evolution-cycle.js +150 -0
  24. package/src/agent/tools/qc-sample.js +94 -0
  25. package/src/agent/tools/registry.js +55 -0
  26. package/src/agent/tools/rule-catalog.js +113 -0
  27. package/src/agent/tools/sandbox-exec.js +106 -0
  28. package/src/agent/tools/tier-downgrade.js +114 -0
  29. package/src/agent/tools/worker-llm-call.js +109 -0
  30. package/src/agent/tools/workflow-run.js +138 -0
  31. package/src/agent/tools/workspace-file.js +122 -0
  32. package/src/agent/version-manager.js +130 -0
  33. package/src/agent/workspace.js +82 -0
  34. package/src/cli/components.js +164 -0
  35. package/src/cli/index.js +329 -0
  36. package/src/cli/init.js +80 -0
  37. package/src/cli/onboard.js +182 -0
  38. package/src/cli/terminal.js +143 -0
  39. package/src/config.js +93 -0
  40. package/template/.env.template +31 -0
  41. package/template/CLAUDE.md +137 -0
  42. package/template/Input/.gitkeep +0 -0
  43. package/template/Output/.gitkeep +0 -0
  44. package/template/Rules/.gitkeep +0 -0
  45. package/template/Samples/.gitkeep +0 -0
  46. package/template/skills/en/meta/compliance-judgment/SKILL.md +114 -0
  47. package/template/skills/en/meta/compliance-judgment/references/output-format.md +151 -0
  48. package/template/skills/en/meta/confidence-system/SKILL.md +117 -0
  49. package/template/skills/en/meta/corner-case-management/SKILL.md +111 -0
  50. package/template/skills/en/meta/cross-document-verification/SKILL.md +131 -0
  51. package/template/skills/en/meta/cross-document-verification/references/contradiction-taxonomy.md +73 -0
  52. package/template/skills/en/meta/data-sensibility/SKILL.md +115 -0
  53. package/template/skills/en/meta/document-parsing/SKILL.md +108 -0
  54. package/template/skills/en/meta/document-parsing/references/parser-catalog.md +40 -0
  55. package/template/skills/en/meta/entity-extraction/SKILL.md +129 -0
  56. package/template/skills/en/meta/tree-processing/SKILL.md +103 -0
  57. package/template/skills/en/meta-meta/bootstrap-workspace/SKILL.md +70 -0
  58. package/template/skills/en/meta-meta/dashboard-reporting/SKILL.md +106 -0
  59. package/template/skills/en/meta-meta/dashboard-reporting/scripts/generate_dashboard.py +178 -0
  60. package/template/skills/en/meta-meta/evolution-loop/SKILL.md +210 -0
  61. package/template/skills/en/meta-meta/evolution-loop/references/convergence-guide.md +62 -0
  62. package/template/skills/en/meta-meta/quality-control/SKILL.md +138 -0
  63. package/template/skills/en/meta-meta/quality-control/references/qa-layers.md +92 -0
  64. package/template/skills/en/meta-meta/quality-control/references/sampling-strategies.md +76 -0
  65. package/template/skills/en/meta-meta/rule-extraction/SKILL.md +100 -0
  66. package/template/skills/en/meta-meta/rule-extraction/references/chunking-strategies.md +80 -0
  67. package/template/skills/en/meta-meta/rule-graph/SKILL.md +118 -0
  68. package/template/skills/en/meta-meta/skill-authoring/SKILL.md +108 -0
  69. package/template/skills/en/meta-meta/skill-authoring/references/skill-format-spec.md +78 -0
  70. package/template/skills/en/meta-meta/skill-to-workflow/SKILL.md +150 -0
  71. package/template/skills/en/meta-meta/skill-to-workflow/references/worker-llm-catalog.md +50 -0
  72. package/template/skills/en/meta-meta/task-decomposition/SKILL.md +129 -0
  73. package/template/skills/en/meta-meta/task-decomposition/references/decision-matrix.md +81 -0
  74. package/template/skills/en/meta-meta/version-control/SKILL.md +152 -0
  75. package/template/skills/en/meta-meta/version-control/references/trace-id-spec.md +79 -0
  76. package/template/skills/en/skill-creator/LICENSE.txt +202 -0
  77. package/template/skills/en/skill-creator/SKILL.md +479 -0
  78. package/template/skills/en/skill-creator/agents/analyzer.md +274 -0
  79. package/template/skills/en/skill-creator/agents/comparator.md +202 -0
  80. package/template/skills/en/skill-creator/agents/grader.md +223 -0
  81. package/template/skills/en/skill-creator/assets/eval_review.html +146 -0
  82. package/template/skills/en/skill-creator/eval-viewer/generate_review.py +471 -0
  83. package/template/skills/en/skill-creator/eval-viewer/viewer.html +1325 -0
  84. package/template/skills/en/skill-creator/references/schemas.md +430 -0
  85. package/template/skills/en/skill-creator/scripts/__init__.py +0 -0
  86. package/template/skills/en/skill-creator/scripts/aggregate_benchmark.py +401 -0
  87. package/template/skills/en/skill-creator/scripts/generate_report.py +326 -0
  88. package/template/skills/en/skill-creator/scripts/improve_description.py +248 -0
  89. package/template/skills/en/skill-creator/scripts/package_skill.py +136 -0
  90. package/template/skills/en/skill-creator/scripts/quick_validate.py +103 -0
  91. package/template/skills/en/skill-creator/scripts/run_eval.py +310 -0
  92. package/template/skills/en/skill-creator/scripts/run_loop.py +332 -0
  93. package/template/skills/en/skill-creator/scripts/utils.py +47 -0
  94. package/template/skills/zh/meta/compliance-judgment/SKILL.md +303 -0
  95. package/template/skills/zh/meta/compliance-judgment/references/output-format.md +151 -0
  96. package/template/skills/zh/meta/confidence-system/SKILL.md +228 -0
  97. package/template/skills/zh/meta/corner-case-management/SKILL.md +235 -0
  98. package/template/skills/zh/meta/cross-document-verification/SKILL.md +241 -0
  99. package/template/skills/zh/meta/cross-document-verification/references/contradiction-taxonomy.md +73 -0
  100. package/template/skills/zh/meta/data-sensibility/SKILL.md +235 -0
  101. package/template/skills/zh/meta/document-parsing/SKILL.md +168 -0
  102. package/template/skills/zh/meta/document-parsing/references/parser-catalog.md +40 -0
  103. package/template/skills/zh/meta/entity-extraction/SKILL.md +276 -0
  104. package/template/skills/zh/meta/tree-processing/SKILL.md +233 -0
  105. package/template/skills/zh/meta-meta/bootstrap-workspace/SKILL.md +147 -0
  106. package/template/skills/zh/meta-meta/dashboard-reporting/SKILL.md +281 -0
  107. package/template/skills/zh/meta-meta/dashboard-reporting/scripts/generate_dashboard.py +178 -0
  108. package/template/skills/zh/meta-meta/evolution-loop/SKILL.md +302 -0
  109. package/template/skills/zh/meta-meta/evolution-loop/references/convergence-guide.md +62 -0
  110. package/template/skills/zh/meta-meta/quality-control/SKILL.md +269 -0
  111. package/template/skills/zh/meta-meta/quality-control/references/qa-layers.md +92 -0
  112. package/template/skills/zh/meta-meta/quality-control/references/sampling-strategies.md +76 -0
  113. package/template/skills/zh/meta-meta/rule-extraction/SKILL.md +208 -0
  114. package/template/skills/zh/meta-meta/rule-extraction/references/chunking-strategies.md +80 -0
  115. package/template/skills/zh/meta-meta/rule-graph/SKILL.md +203 -0
  116. package/template/skills/zh/meta-meta/skill-authoring/SKILL.md +235 -0
  117. package/template/skills/zh/meta-meta/skill-authoring/references/skill-format-spec.md +78 -0
  118. package/template/skills/zh/meta-meta/skill-to-workflow/SKILL.md +275 -0
  119. package/template/skills/zh/meta-meta/skill-to-workflow/references/worker-llm-catalog.md +50 -0
  120. package/template/skills/zh/meta-meta/task-decomposition/SKILL.md +224 -0
  121. package/template/skills/zh/meta-meta/task-decomposition/references/decision-matrix.md +81 -0
  122. package/template/skills/zh/meta-meta/version-control/SKILL.md +284 -0
  123. package/template/skills/zh/meta-meta/version-control/references/trace-id-spec.md +79 -0
  124. package/template/skills/zh/skill-creator/LICENSE.txt +202 -0
  125. package/template/skills/zh/skill-creator/SKILL.md +479 -0
  126. package/template/skills/zh/skill-creator/agents/analyzer.md +274 -0
  127. package/template/skills/zh/skill-creator/agents/comparator.md +202 -0
  128. package/template/skills/zh/skill-creator/agents/grader.md +223 -0
  129. package/template/skills/zh/skill-creator/assets/eval_review.html +146 -0
  130. package/template/skills/zh/skill-creator/eval-viewer/generate_review.py +471 -0
  131. package/template/skills/zh/skill-creator/eval-viewer/viewer.html +1325 -0
  132. package/template/skills/zh/skill-creator/references/schemas.md +430 -0
  133. package/template/skills/zh/skill-creator/scripts/__init__.py +0 -0
  134. package/template/skills/zh/skill-creator/scripts/aggregate_benchmark.py +401 -0
  135. package/template/skills/zh/skill-creator/scripts/generate_report.py +326 -0
  136. package/template/skills/zh/skill-creator/scripts/improve_description.py +248 -0
  137. package/template/skills/zh/skill-creator/scripts/package_skill.py +136 -0
  138. package/template/skills/zh/skill-creator/scripts/quick_validate.py +103 -0
  139. package/template/skills/zh/skill-creator/scripts/run_eval.py +310 -0
  140. package/template/skills/zh/skill-creator/scripts/run_loop.py +332 -0
  141. package/template/skills/zh/skill-creator/scripts/utils.py +47 -0
@@ -0,0 +1,329 @@
1
+ import React, { useState, useEffect, useCallback, useRef } from "react";
2
+ import { render, Box, Text, useApp, useInput } from "ink";
3
+ import { loadSettings } from "../config.js";
4
+ import { LLMClient } from "../agent/llm-client.js";
5
+ import { AgentEngine } from "../agent/engine.js";
6
+ import { Workspace } from "../agent/workspace.js";
7
+ import { ConversationHistory } from "../agent/history.js";
8
+ import {
9
+ WelcomeBanner,
10
+ StatusBar,
11
+ CookingSpinner,
12
+ ToolBlock,
13
+ HRule,
14
+ InputPrompt,
15
+ } from "./components.js";
16
+
17
+ const h = React.createElement;
18
+
19
+ /**
20
+ * Main KC Agent CLI App using Ink (React for terminals).
21
+ */
22
+ function App({ engine, config }) {
23
+ const { exit } = useApp();
24
+ const [messages, setMessages] = useState([]);
25
+ const [inputValue, setInputValue] = useState("");
26
+ const [streaming, setStreaming] = useState(false);
27
+ const [streamingText, setStreamingText] = useState("");
28
+ const [currentTool, setCurrentTool] = useState(null);
29
+ const [sessionId, setSessionId] = useState(engine.workspace.sessionId);
30
+ const [phase, setPhase] = useState(engine.currentPhase);
31
+ const [showWelcome, setShowWelcome] = useState(true);
32
+
33
+ const engineRef = useRef(engine);
34
+ const streamingRef = useRef(false);
35
+ const queueRef = useRef([]);
36
+
37
+ const addMessage = useCallback((msg) => {
38
+ setMessages((prev) => [...prev, msg]);
39
+ }, []);
40
+
41
+ const runTurn = useCallback(async (text) => {
42
+ streamingRef.current = true;
43
+ setStreaming(true);
44
+ setStreamingText("");
45
+ setCurrentTool(null);
46
+
47
+ let accumulated = "";
48
+
49
+ try {
50
+ for await (const event of engineRef.current.runTurn(text)) {
51
+ switch (event.type) {
52
+ case "text_delta":
53
+ accumulated += event.text ?? "";
54
+ setStreamingText(accumulated);
55
+ break;
56
+
57
+ case "turn_complete":
58
+ if (accumulated) {
59
+ addMessage({ role: "agent", content: accumulated });
60
+ }
61
+ accumulated = "";
62
+ setStreamingText("");
63
+ setCurrentTool(null);
64
+ break;
65
+
66
+ case "tool_start":
67
+ // Flush any accumulated text before tool
68
+ if (accumulated) {
69
+ addMessage({ role: "agent", content: accumulated });
70
+ accumulated = "";
71
+ setStreamingText("");
72
+ }
73
+ setCurrentTool({ name: event.name, input: event.input, output: null, isError: false, isRunning: true });
74
+ break;
75
+
76
+ case "tool_result":
77
+ // Add completed tool to messages
78
+ addMessage({
79
+ role: "tool",
80
+ toolName: event.name,
81
+ toolInput: currentTool?.input ?? event.input,
82
+ toolOutput: event.output,
83
+ toolIsError: event.isError,
84
+ });
85
+ setCurrentTool(null);
86
+ break;
87
+
88
+ case "pipeline_event": {
89
+ const nextPhase = event.data?.nextPhase ?? event.data?.next_phase ?? "";
90
+ if (nextPhase) setPhase(nextPhase);
91
+ addMessage({ role: "system", content: `[Pipeline] ${event.data?.message ?? ""}` });
92
+ break;
93
+ }
94
+
95
+ case "error":
96
+ addMessage({ role: "system", content: `Error: ${event.message ?? "Unknown error"}` });
97
+ break;
98
+ }
99
+ }
100
+ } catch (err) {
101
+ addMessage({ role: "system", content: `Error: ${err.message}` });
102
+ }
103
+
104
+ streamingRef.current = false;
105
+ setStreaming(false);
106
+
107
+ // Process queue
108
+ if (queueRef.current.length > 0) {
109
+ const next = queueRef.current.shift();
110
+ runTurn(next);
111
+ }
112
+ }, [addMessage]);
113
+
114
+ const handleSlashCommand = useCallback((text) => {
115
+ const parts = text.split(/\s+/);
116
+ const cmd = parts[0].toLowerCase();
117
+ const arg = parts.slice(1).join(" ").trim();
118
+
119
+ switch (cmd) {
120
+ case "/help":
121
+ addMessage({
122
+ role: "system",
123
+ content:
124
+ "Commands:\n" +
125
+ " /help Show this help\n" +
126
+ " /status Show session info, model, phase, workspace\n" +
127
+ " /clear Clear conversation history (keep workspace)\n" +
128
+ " /sessions List all sessions\n" +
129
+ " /resume <name> Resume a previous session\n" +
130
+ " /rename <name> Rename current session\n" +
131
+ " /exit Quit",
132
+ });
133
+ return true;
134
+
135
+ case "/status":
136
+ addMessage({
137
+ role: "system",
138
+ content:
139
+ `Session: ${engineRef.current.workspace.sessionId}\n` +
140
+ `Phase: ${engineRef.current.currentPhase.toUpperCase()}\n` +
141
+ `Model: ${config.kcModel}\n` +
142
+ `LLM URL: ${config.llmBaseUrl}\n` +
143
+ `Workspace: ${engineRef.current.workspace.cwd}\n` +
144
+ `Tools: ${engineRef.current.toolRegistry.size} registered\n` +
145
+ `History: ${engineRef.current.history.messages.length} messages`,
146
+ });
147
+ return true;
148
+
149
+ case "/clear":
150
+ engineRef.current.history = new ConversationHistory(engineRef.current.workspace.cwd);
151
+ setMessages([]);
152
+ addMessage({ role: "system", content: "Conversation cleared. Workspace and pipeline state preserved." });
153
+ return true;
154
+
155
+ case "/rename":
156
+ if (!arg) {
157
+ addMessage({ role: "system", content: "Usage: /rename <new_name>" });
158
+ } else {
159
+ try {
160
+ const newId = engineRef.current.workspace.rename(arg);
161
+ setSessionId(newId);
162
+ addMessage({ role: "system", content: `Session renamed to: ${newId}` });
163
+ } catch (err) {
164
+ addMessage({ role: "system", content: `Rename failed: ${err.message}` });
165
+ }
166
+ }
167
+ return true;
168
+
169
+ case "/sessions": {
170
+ const sessions = Workspace.listSessions(config.kcWorkspaceRoot);
171
+ if (sessions.length === 0) {
172
+ addMessage({ role: "system", content: "No sessions found." });
173
+ } else {
174
+ const lines = sessions.map((s) => {
175
+ const marker = s.id === engineRef.current.workspace.sessionId ? " ← current" : "";
176
+ return ` ${s.id}${marker}`;
177
+ });
178
+ addMessage({ role: "system", content: "Sessions:\n" + lines.join("\n") });
179
+ }
180
+ return true;
181
+ }
182
+
183
+ case "/resume":
184
+ if (!arg) {
185
+ const sessions = Workspace.listSessions(config.kcWorkspaceRoot);
186
+ if (sessions.length === 0) {
187
+ addMessage({ role: "system", content: "No sessions found." });
188
+ } else {
189
+ const lines = sessions.map((s) => {
190
+ const marker = s.id === engineRef.current.workspace.sessionId ? " ← current" : "";
191
+ return ` ${s.id}${marker}`;
192
+ });
193
+ addMessage({ role: "system", content: "Sessions:\n" + lines.join("\n") + "\n\nUsage: /resume <name>" });
194
+ }
195
+ } else {
196
+ addMessage({ role: "system", content: `Resuming session: ${arg} (not yet implemented)` });
197
+ }
198
+ return true;
199
+
200
+ case "/exit":
201
+ case "/quit":
202
+ exit();
203
+ return true;
204
+
205
+ default:
206
+ return false;
207
+ }
208
+ }, [addMessage, config, exit]);
209
+
210
+ const handleSubmit = useCallback((text) => {
211
+ const trimmed = text.trim();
212
+ setInputValue("");
213
+ if (!trimmed) return;
214
+
215
+ addMessage({ role: "user", content: trimmed });
216
+
217
+ if (trimmed.startsWith("/")) {
218
+ const handled = handleSlashCommand(trimmed);
219
+ if (handled) return;
220
+ }
221
+
222
+ if (streamingRef.current) {
223
+ queueRef.current.push(trimmed);
224
+ } else {
225
+ runTurn(trimmed);
226
+ }
227
+ }, [addMessage, handleSlashCommand, runTurn]);
228
+
229
+ // Handle Ctrl+C and Ctrl+D
230
+ useInput((input, key) => {
231
+ if (key.ctrl && input === "c") {
232
+ if (streamingRef.current) {
233
+ queueRef.current.length = 0;
234
+ addMessage({ role: "system", content: "[Queue cleared]" });
235
+ } else {
236
+ exit();
237
+ }
238
+ }
239
+ if (key.ctrl && input === "d") {
240
+ exit();
241
+ }
242
+ });
243
+
244
+ return h(Box, { flexDirection: "column" },
245
+ // Welcome banner
246
+ showWelcome ? h(WelcomeBanner) : null,
247
+
248
+ // Message history
249
+ ...messages.map((msg, i) => {
250
+ if (msg.role === "user") {
251
+ return h(Box, { key: `msg-${i}` },
252
+ h(Text, { dimColor: true }, "❯ "),
253
+ h(Text, null, msg.content),
254
+ );
255
+ }
256
+ if (msg.role === "agent") {
257
+ return h(Box, { key: `msg-${i}` },
258
+ h(Text, null, msg.content),
259
+ );
260
+ }
261
+ if (msg.role === "tool") {
262
+ return h(ToolBlock, {
263
+ key: `msg-${i}`,
264
+ name: msg.toolName,
265
+ input: msg.toolInput,
266
+ output: msg.toolOutput,
267
+ isError: msg.toolIsError,
268
+ isRunning: false,
269
+ });
270
+ }
271
+ if (msg.role === "system") {
272
+ return h(Box, { key: `msg-${i}` },
273
+ h(Text, { dimColor: true }, msg.content),
274
+ );
275
+ }
276
+ return null;
277
+ }),
278
+
279
+ // Currently streaming text
280
+ streamingText ? h(Box, { key: "streaming" },
281
+ h(Text, null, streamingText),
282
+ ) : null,
283
+
284
+ // Currently running tool
285
+ currentTool ? h(ToolBlock, {
286
+ key: "current-tool",
287
+ name: currentTool.name,
288
+ input: currentTool.input,
289
+ output: null,
290
+ isError: false,
291
+ isRunning: true,
292
+ }) : null,
293
+
294
+ // Spinner while waiting
295
+ streaming && !streamingText && !currentTool
296
+ ? h(CookingSpinner)
297
+ : null,
298
+
299
+ // Separator + Input
300
+ h(HRule),
301
+ h(InputPrompt, {
302
+ value: inputValue,
303
+ onChange: setInputValue,
304
+ onSubmit: handleSubmit,
305
+ isActive: !streaming,
306
+ }),
307
+ h(HRule),
308
+ h(StatusBar, { sessionId, phase }),
309
+ );
310
+ }
311
+
312
+ export async function main() {
313
+ const config = loadSettings();
314
+
315
+ if (!config.llmApiKey) {
316
+ console.error("Error: No API key configured. Run 'kc-beta onboard' first.");
317
+ process.exit(1);
318
+ }
319
+
320
+ const client = new LLMClient({
321
+ apiKey: config.llmApiKey,
322
+ baseUrl: config.llmBaseUrl,
323
+ });
324
+
325
+ const engine = new AgentEngine({ client, config });
326
+
327
+ const instance = render(h(App, { engine, config }));
328
+ await instance.waitUntilExit();
329
+ }
@@ -0,0 +1,80 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ const TEMPLATE_DIR = path.resolve(__dirname, "../../template");
7
+
8
+ /**
9
+ * kc-beta init [project-name] [--lang=zh]
10
+ * Creates a new workspace from the bundled template.
11
+ */
12
+ export async function init() {
13
+ const args = process.argv.slice(3);
14
+ let projectName = "kc-project";
15
+ let lang = "en";
16
+
17
+ for (const arg of args) {
18
+ if (arg.startsWith("--lang=")) {
19
+ lang = arg.split("=")[1] || "en";
20
+ } else if (!arg.startsWith("-")) {
21
+ projectName = arg;
22
+ }
23
+ }
24
+
25
+ const targetDir = path.resolve(process.cwd(), projectName);
26
+
27
+ if (fs.existsSync(targetDir)) {
28
+ console.error(`Error: Directory '${projectName}' already exists.`);
29
+ process.exit(1);
30
+ }
31
+
32
+ if (!fs.existsSync(TEMPLATE_DIR)) {
33
+ console.error("Error: Template directory not found. Ensure kc-beta is installed correctly.");
34
+ process.exit(1);
35
+ }
36
+
37
+ // Copy template
38
+ copyDir(TEMPLATE_DIR, targetDir);
39
+
40
+ // Remove opposite language skills
41
+ const skillsDir = path.join(targetDir, "skills");
42
+ if (fs.existsSync(skillsDir)) {
43
+ const removeLang = lang === "zh" ? "en" : "zh";
44
+ const removePath = path.join(skillsDir, removeLang);
45
+ if (fs.existsSync(removePath)) {
46
+ fs.rmSync(removePath, { recursive: true, force: true });
47
+ }
48
+ }
49
+
50
+ // Rename .env.template to .env
51
+ const envTemplate = path.join(targetDir, ".env.template");
52
+ const envTarget = path.join(targetDir, ".env");
53
+ if (fs.existsSync(envTemplate)) {
54
+ fs.renameSync(envTemplate, envTarget);
55
+ // Set language
56
+ let content = fs.readFileSync(envTarget, "utf-8");
57
+ content = content.replace("LANGUAGE=en", `LANGUAGE=${lang}`);
58
+ fs.writeFileSync(envTarget, content, "utf-8");
59
+ }
60
+
61
+ console.log(`\n Created ${projectName}/`);
62
+ console.log(` Language: ${lang === "zh" ? "中文" : "English"}`);
63
+ console.log(`\n Next steps:`);
64
+ console.log(` cd ${projectName}`);
65
+ console.log(` kc-beta onboard # configure API keys`);
66
+ console.log(` kc-beta # start the agent\n`);
67
+ }
68
+
69
+ function copyDir(src, dest) {
70
+ fs.mkdirSync(dest, { recursive: true });
71
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
72
+ const srcPath = path.join(src, entry.name);
73
+ const destPath = path.join(dest, entry.name);
74
+ if (entry.isDirectory()) {
75
+ copyDir(srcPath, destPath);
76
+ } else {
77
+ fs.copyFileSync(srcPath, destPath);
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,182 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import readline from "node:readline";
4
+ import os from "node:os";
5
+
6
+ const CONFIG_DIR = path.join(os.homedir(), ".kc_agent");
7
+ const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
8
+
9
+ const ESC = "\x1b[";
10
+ const RESET = `${ESC}0m`;
11
+ const BOLD = `${ESC}1m`;
12
+ const DIM = `${ESC}2m`;
13
+ const GREEN = `${ESC}32m`;
14
+ const CYAN = `${ESC}36m`;
15
+ const GRAY = `${ESC}90m`;
16
+ const YELLOW = `${ESC}33m`;
17
+ const RED = `${ESC}31m`;
18
+
19
+ const L = {
20
+ en: {
21
+ title: "KC Agent Setup",
22
+ existingConfig: "Existing config found. Press Enter to keep current values.",
23
+ langPrompt: "Language",
24
+ langOptions: ["English", "中文"],
25
+ providerPrompt: "LLM Provider",
26
+ providerLabels: [
27
+ "SiliconFlow (recommended for China)",
28
+ "Aliyun Bailian",
29
+ "Anthropic",
30
+ "OpenAI",
31
+ "Custom (enter base URL)",
32
+ ],
33
+ current: "current",
34
+ choose: "Choose",
35
+ baseUrl: "Base URL",
36
+ baseUrlRequired: "Base URL is required for custom provider.",
37
+ apiKey: "API Key",
38
+ apiKeyRequired: "required",
39
+ apiKeyKeep: "Enter to keep",
40
+ apiKeyMissing: "API key is required. Run 'kc-beta onboard' again.",
41
+ conductorModel: "Conductor Model",
42
+ workerTiers: "Worker LLM Tiers",
43
+ tierHint: "Enter to accept defaults",
44
+ accuracy: "Accuracy Threshold",
45
+ saved: "Saved to",
46
+ runHint: "Run {cmd} to start the agent.",
47
+ },
48
+ zh: {
49
+ title: "KC Agent 配置向导",
50
+ existingConfig: "检测到已有配置。按回车保留当前值。",
51
+ langPrompt: "语言",
52
+ langOptions: ["English", "中文"],
53
+ providerPrompt: "大模型服务商",
54
+ providerLabels: [
55
+ "SiliconFlow(国内推荐)",
56
+ "阿里云百炼",
57
+ "Anthropic",
58
+ "OpenAI",
59
+ "自定义(输入接口地址)",
60
+ ],
61
+ current: "当前",
62
+ choose: "选择",
63
+ baseUrl: "接口地址",
64
+ baseUrlRequired: "自定义服务商必须填写接口地址。",
65
+ apiKey: "API 密钥",
66
+ apiKeyRequired: "必填",
67
+ apiKeyKeep: "回车保留当前密钥",
68
+ apiKeyMissing: "API 密钥为必填项。请重新运行 'kc-beta onboard'。",
69
+ conductorModel: "主模型",
70
+ workerTiers: "Worker 模型分层",
71
+ tierHint: "回车接受默认值",
72
+ accuracy: "准确率阈值",
73
+ saved: "已保存至",
74
+ runHint: "运行 {cmd} 启动 Agent。",
75
+ },
76
+ };
77
+
78
+ const PROVIDERS = [
79
+ { name: "SiliconFlow", base_url: "https://api.siliconflow.cn/v1", model: "Pro/zai-org/GLM-5",
80
+ tiers: { tier1: "Pro/zai-org/GLM-5, Pro/moonshotai/Kimi-K2.5", tier2: "Pro/deepseek-ai/DeepSeek-V3.2, Pro/MiniMaxAI/MiniMax-M2.5", tier3: "Qwen/Qwen3.5-122B-A10B", tier4: "Qwen/Qwen3.5-35B-A3B" } },
81
+ { name: "Aliyun", base_url: "https://coding.dashscope.aliyuncs.com/v1", model: "glm-5",
82
+ tiers: { tier1: "glm-5", tier2: "deepseek-v3", tier3: "qwen-plus", tier4: "qwen-turbo" } },
83
+ { name: "Anthropic", base_url: "https://api.anthropic.com/v1", model: "claude-sonnet-4-20250514",
84
+ tiers: { tier1: "claude-sonnet-4-20250514", tier2: "claude-sonnet-4-20250514", tier3: "claude-haiku-4-5-20251001", tier4: "claude-haiku-4-5-20251001" } },
85
+ { name: "OpenAI", base_url: "https://api.openai.com/v1", model: "gpt-4o",
86
+ tiers: { tier1: "gpt-4o", tier2: "gpt-4o-mini", tier3: "gpt-4o-mini", tier4: "gpt-4o-mini" } },
87
+ { name: "Custom", base_url: "", model: "", tiers: { tier1: "", tier2: "", tier3: "", tier4: "" } },
88
+ ];
89
+
90
+ function ask(rl, question, defaultValue = "") {
91
+ const suffix = defaultValue ? ` ${DIM}[${defaultValue}]${RESET}` : "";
92
+ return new Promise((resolve) => {
93
+ rl.question(`${question}${suffix}: `, (answer) => resolve(answer.trim() || defaultValue));
94
+ });
95
+ }
96
+
97
+ export async function onboard() {
98
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
99
+
100
+ let existing = {};
101
+ if (fs.existsSync(CONFIG_PATH)) {
102
+ try { existing = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8")); } catch { /* ignore */ }
103
+ }
104
+
105
+ console.log();
106
+ console.log(` ${BOLD}KC Agent Setup / KC Agent 配置向导${RESET}`);
107
+ console.log(` ${GRAY}${"─".repeat(40)}${RESET}`);
108
+ console.log();
109
+ console.log(` ${CYAN}Language / 语言:${RESET}`);
110
+ console.log(` 1. English`);
111
+ console.log(` 2. 中文`);
112
+ const langDefault = existing.language === "zh" ? "2" : "1";
113
+ const langChoice = await ask(rl, ` ${GRAY}>${RESET} Choose / 选择`, langDefault);
114
+ const lang = langChoice === "2" ? "zh" : "en";
115
+ const t = L[lang];
116
+ console.log();
117
+
118
+ if (Object.keys(existing).length > 0) {
119
+ console.log(` ${DIM}${t.existingConfig}${RESET}`);
120
+ console.log();
121
+ }
122
+
123
+ console.log(` ${CYAN}${t.providerPrompt}:${RESET}`);
124
+ for (let i = 0; i < PROVIDERS.length; i++) {
125
+ const marker = PROVIDERS[i].name.toLowerCase() === existing.provider ? ` ${GREEN}(${t.current})${RESET}` : "";
126
+ console.log(` ${i + 1}. ${t.providerLabels[i]}${marker}`);
127
+ }
128
+ const providerIdx = parseInt(await ask(rl, ` ${GRAY}>${RESET} ${t.choose}`, "1"), 10) - 1;
129
+ const provider = PROVIDERS[Math.max(0, Math.min(providerIdx, PROVIDERS.length - 1))];
130
+ console.log();
131
+
132
+ let baseUrl = provider.base_url;
133
+ if (provider.name === "Custom") {
134
+ baseUrl = await ask(rl, ` ${t.baseUrl}`, existing.base_url || "");
135
+ if (!baseUrl) { console.log(` ${RED}${t.baseUrlRequired}${RESET}`); rl.close(); process.exit(1); }
136
+ }
137
+
138
+ const maskedExisting = existing.api_key ? existing.api_key.slice(0, 6) + "..." + existing.api_key.slice(-4) : "";
139
+ const keyPrompt = maskedExisting
140
+ ? ` ${CYAN}${t.apiKey}${RESET} ${DIM}(${maskedExisting}, ${t.apiKeyKeep})${RESET}`
141
+ : ` ${CYAN}${t.apiKey}${RESET} ${YELLOW}(${t.apiKeyRequired})${RESET}`;
142
+ const apiKey = await ask(rl, keyPrompt, "");
143
+ const finalKey = apiKey || existing.api_key || "";
144
+ if (!finalKey) { console.log(` ${RED}${t.apiKeyMissing}${RESET}`); rl.close(); process.exit(1); }
145
+ console.log();
146
+
147
+ const defaultModel = provider.model || existing.conductor_model || "";
148
+ const model = await ask(rl, ` ${CYAN}${t.conductorModel}${RESET}`, defaultModel);
149
+ console.log();
150
+
151
+ console.log(` ${CYAN}${t.workerTiers}${RESET} ${DIM}(${t.tierHint})${RESET}`);
152
+ const tiers = {};
153
+ for (const tier of ["tier1", "tier2", "tier3", "tier4"]) {
154
+ const def = provider.tiers[tier] || existing?.tiers?.[tier] || "";
155
+ tiers[tier] = await ask(rl, ` ${tier.toUpperCase()}`, def);
156
+ }
157
+ console.log();
158
+
159
+ const defaultAcc = existing.accuracy_threshold?.toString() || "0.9";
160
+ const accuracy = parseFloat(await ask(rl, ` ${CYAN}${t.accuracy}${RESET}`, defaultAcc));
161
+ console.log();
162
+
163
+ rl.close();
164
+
165
+ const config = {
166
+ language: lang,
167
+ provider: provider.name.toLowerCase(),
168
+ api_key: finalKey,
169
+ base_url: baseUrl,
170
+ conductor_model: model,
171
+ tiers,
172
+ accuracy_threshold: accuracy,
173
+ };
174
+
175
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
176
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
177
+
178
+ console.log(` ${GREEN}✓${RESET} ${t.saved} ${GRAY}${CONFIG_PATH}${RESET}`);
179
+ console.log();
180
+ console.log(` ${t.runHint.replace("{cmd}", `${BOLD}kc-beta${RESET}`)}`);
181
+ console.log();
182
+ }