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,101 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Manages the message list for the OpenAI-compatible API.
6
+ * Persists to workspace/logs/conversation/ on every write.
7
+ * Loads existing history when workspacePath is provided.
8
+ */
9
+ export class ConversationHistory {
10
+ /**
11
+ * @param {string} [workspacePath] - Workspace directory for persistence
12
+ */
13
+ constructor(workspacePath) {
14
+ /** @type {Array<object>} API messages */
15
+ this._messages = [];
16
+ /** @type {Array<object>} Flat display log for replay */
17
+ this._displayLog = [];
18
+ this._workspacePath = workspacePath || null;
19
+
20
+ if (this._workspacePath) this._load();
21
+ }
22
+
23
+ get messages() { return this._messages; }
24
+ get displayLog() { return this._displayLog; }
25
+
26
+ addUser(text) {
27
+ this._messages.push({ role: "user", content: text });
28
+ this._displayLog.push({ role: "user", content: text });
29
+ this._save();
30
+ }
31
+
32
+ /**
33
+ * Add a pre-built message dict (assistant with tool_calls, tool results, etc.)
34
+ * @param {object} message
35
+ */
36
+ addRaw(message) {
37
+ this._messages.push(message);
38
+
39
+ const role = message.role || "";
40
+ if (role === "assistant") {
41
+ const content = message.content || "";
42
+ if (content) {
43
+ this._displayLog.push({ role: "agent", content });
44
+ }
45
+ for (const tc of message.tool_calls || []) {
46
+ const fn = tc.function || {};
47
+ let toolInput = {};
48
+ try { toolInput = JSON.parse(fn.arguments || "{}"); } catch { /* ignore */ }
49
+ this._displayLog.push({
50
+ role: "tool",
51
+ toolName: fn.name || "",
52
+ toolInput,
53
+ });
54
+ }
55
+ } else if (role === "tool") {
56
+ const content = message.content || "";
57
+ // Update the last tool entry with output
58
+ for (let i = this._displayLog.length - 1; i >= 0; i--) {
59
+ if (this._displayLog[i].role === "tool" && !("toolOutput" in this._displayLog[i])) {
60
+ this._displayLog[i].toolOutput = content;
61
+ break;
62
+ }
63
+ }
64
+ }
65
+
66
+ this._save();
67
+ }
68
+
69
+ _save() {
70
+ if (!this._workspacePath) return;
71
+ const convDir = path.join(this._workspacePath, "logs", "conversation");
72
+ fs.mkdirSync(convDir, { recursive: true });
73
+ fs.writeFileSync(
74
+ path.join(convDir, "messages.json"),
75
+ JSON.stringify(this._messages, null, 2),
76
+ "utf-8",
77
+ );
78
+ fs.writeFileSync(
79
+ path.join(convDir, "display.json"),
80
+ JSON.stringify(this._displayLog, null, 2),
81
+ "utf-8",
82
+ );
83
+ }
84
+
85
+ _load() {
86
+ if (!this._workspacePath) return;
87
+ const convDir = path.join(this._workspacePath, "logs", "conversation");
88
+
89
+ const msgPath = path.join(convDir, "messages.json");
90
+ if (fs.existsSync(msgPath)) {
91
+ try { this._messages = JSON.parse(fs.readFileSync(msgPath, "utf-8")); }
92
+ catch { this._messages = []; }
93
+ }
94
+
95
+ const displayPath = path.join(convDir, "display.json");
96
+ if (fs.existsSync(displayPath)) {
97
+ try { this._displayLog = JSON.parse(fs.readFileSync(displayPath, "utf-8")); }
98
+ catch { this._displayLog = []; }
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Thin LLM client using native fetch + SSE parsing.
3
+ * Replaces the Python openai.AsyncOpenAI client.
4
+ * Supports OpenAI-compatible APIs (SiliconFlow, Aliyun, OpenAI, etc.)
5
+ */
6
+ export class LLMClient {
7
+ /**
8
+ * @param {object} opts
9
+ * @param {string} opts.apiKey
10
+ * @param {string} opts.baseUrl - e.g. "https://api.siliconflow.cn/v1"
11
+ */
12
+ constructor({ apiKey, baseUrl }) {
13
+ this.apiKey = apiKey;
14
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
15
+ }
16
+
17
+ /**
18
+ * Streaming chat completion. Yields parsed SSE chunk objects.
19
+ * @param {object} opts
20
+ * @param {string} opts.model
21
+ * @param {Array} opts.messages
22
+ * @param {Array} [opts.tools]
23
+ * @param {number} [opts.maxTokens]
24
+ * @yields {object} Parsed chunk from the SSE stream
25
+ */
26
+ async *streamChat({ model, messages, tools, maxTokens }) {
27
+ const body = {
28
+ model,
29
+ messages,
30
+ stream: true,
31
+ };
32
+ if (maxTokens) body.max_tokens = maxTokens;
33
+ if (tools && tools.length > 0) body.tools = tools;
34
+
35
+ const resp = await fetch(`${this.baseUrl}/chat/completions`, {
36
+ method: "POST",
37
+ headers: {
38
+ "Authorization": `Bearer ${this.apiKey}`,
39
+ "Content-Type": "application/json",
40
+ },
41
+ body: JSON.stringify(body),
42
+ });
43
+
44
+ if (!resp.ok) {
45
+ const text = await resp.text();
46
+ throw new Error(`LLM API error ${resp.status}: ${text}`);
47
+ }
48
+
49
+ for await (const data of this._parseSSE(resp.body)) {
50
+ yield data;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Non-streaming chat completion. Returns the full response.
56
+ * @param {object} opts
57
+ * @param {string} opts.model
58
+ * @param {Array} opts.messages
59
+ * @param {number} [opts.maxTokens]
60
+ * @returns {object} The response object
61
+ */
62
+ async chat({ model, messages, maxTokens }) {
63
+ const body = {
64
+ model,
65
+ messages,
66
+ };
67
+ if (maxTokens) body.max_tokens = maxTokens;
68
+
69
+ const resp = await fetch(`${this.baseUrl}/chat/completions`, {
70
+ method: "POST",
71
+ headers: {
72
+ "Authorization": `Bearer ${this.apiKey}`,
73
+ "Content-Type": "application/json",
74
+ },
75
+ body: JSON.stringify(body),
76
+ });
77
+
78
+ if (!resp.ok) {
79
+ const text = await resp.text();
80
+ throw new Error(`LLM API error ${resp.status}: ${text}`);
81
+ }
82
+
83
+ return resp.json();
84
+ }
85
+
86
+ /**
87
+ * Parse SSE stream from a ReadableStream.
88
+ * Handles the `data: {...}` format used by OpenAI-compatible APIs.
89
+ * @param {ReadableStream} body
90
+ * @yields {object} Parsed JSON from each data line
91
+ */
92
+ async *_parseSSE(body) {
93
+ const decoder = new TextDecoder();
94
+ let buffer = "";
95
+
96
+ for await (const chunk of body) {
97
+ buffer += decoder.decode(chunk, { stream: true });
98
+ const lines = buffer.split("\n");
99
+ buffer = lines.pop(); // keep incomplete line
100
+
101
+ for (const line of lines) {
102
+ const trimmed = line.trim();
103
+ if (!trimmed || trimmed.startsWith(":")) continue;
104
+ if (trimmed.startsWith("data: ")) {
105
+ const data = trimmed.slice(6).trim();
106
+ if (data === "[DONE]") return;
107
+ try {
108
+ yield JSON.parse(data);
109
+ } catch {
110
+ // Skip malformed JSON lines
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ // Process any remaining buffer
117
+ if (buffer.trim()) {
118
+ const trimmed = buffer.trim();
119
+ if (trimmed.startsWith("data: ")) {
120
+ const data = trimmed.slice(6).trim();
121
+ if (data !== "[DONE]") {
122
+ try {
123
+ yield JSON.parse(data);
124
+ } catch {
125
+ // Skip
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Base class for all pipeline components.
3
+ * Each pipeline hard-codes one meta-meta skill's methodology as structural code.
4
+ */
5
+ export class Pipeline {
6
+ /** Return context injected into the system prompt before each LLM call. */
7
+ describeState() { throw new Error("Not implemented"); }
8
+
9
+ /** Called after each tool execution. Returns PipelineEvent or null. */
10
+ onToolResult(toolName, toolInput, result) { throw new Error("Not implemented"); }
11
+
12
+ /** Whether all requirements for leaving this phase are satisfied. */
13
+ exitCriteriaMet() { throw new Error("Not implemented"); }
14
+ }
@@ -0,0 +1,113 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { Phase, PipelineEvent } from "./index.js";
4
+ import { Pipeline } from "./base.js";
5
+
6
+ export class DistillationEngine extends Pipeline {
7
+ constructor(workspace) {
8
+ super();
9
+ this._workspace = workspace;
10
+ this.skillsToDistill = [];
11
+ this.workflowsCreated = {};
12
+ this.workflowsTested = {};
13
+ this.workflowsPassing = [];
14
+ this.tierAssignments = {};
15
+ this._workflowAccuracy = 0.9;
16
+ this._scanWorkspace();
17
+ }
18
+
19
+ _scanWorkspace() {
20
+ this._loadConfig();
21
+ this._loadSkills();
22
+ this._scanWorkflows();
23
+ }
24
+
25
+ _loadConfig() {
26
+ const envPath = path.join(this._workspace.cwd, ".env");
27
+ if (!fs.existsSync(envPath)) return;
28
+ for (const line of fs.readFileSync(envPath, "utf-8").split("\n")) {
29
+ if (line.startsWith("WORKFLOW_ACCURACY=")) try { this._workflowAccuracy = parseFloat(line.split("=")[1]); } catch { /* skip */ }
30
+ }
31
+ }
32
+
33
+ _loadSkills() {
34
+ this.skillsToDistill = [];
35
+ const dir = path.join(this._workspace.cwd, "rule_skills");
36
+ if (!fs.existsSync(dir)) return;
37
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
38
+ if (e.isDirectory() && !e.name.startsWith("__")) this.skillsToDistill.push(e.name);
39
+ }
40
+ }
41
+
42
+ _scanWorkflows() {
43
+ this.workflowsCreated = {};
44
+ this.workflowsTested = {};
45
+ this.workflowsPassing = [];
46
+ this.tierAssignments = {};
47
+ const wfDir = path.join(this._workspace.cwd, "workflows");
48
+ if (!fs.existsSync(wfDir)) return;
49
+
50
+ for (const e of fs.readdirSync(wfDir, { withFileTypes: true })) {
51
+ if (e.isDirectory()) {
52
+ const ruleDir = path.join(wfDir, e.name);
53
+ const pyFiles = fs.readdirSync(ruleDir).filter((f) => f.endsWith(".py"));
54
+ if (pyFiles.length > 0) this.workflowsCreated[e.name] = pyFiles.length;
55
+ const cfgPath = path.join(ruleDir, "config.json");
56
+ if (fs.existsSync(cfgPath)) {
57
+ try {
58
+ const cfg = JSON.parse(fs.readFileSync(cfgPath, "utf-8"));
59
+ if (cfg.tier) this.tierAssignments[e.name] = cfg.tier;
60
+ if (cfg.accuracy != null) {
61
+ const acc = parseFloat(cfg.accuracy);
62
+ this.workflowsTested[e.name] = acc;
63
+ if (acc >= this._workflowAccuracy) this.workflowsPassing.push(e.name);
64
+ }
65
+ } catch { /* skip */ }
66
+ }
67
+ } else if (e.isFile() && e.name.endsWith(".py")) {
68
+ this.workflowsCreated[path.parse(e.name).name] = 1;
69
+ }
70
+ }
71
+ }
72
+
73
+ describeState() {
74
+ this._scanWorkspace();
75
+ const total = this.skillsToDistill.length;
76
+ const created = Object.keys(this.workflowsCreated).length;
77
+ const passing = this.workflowsPassing.length;
78
+ const parts = ["## Current Phase: DISTILLATION"];
79
+ parts.push(`### Progress\n- Skills to distill: ${total}\n- Workflows created: ${created}\n- Workflows passing (>=${this._workflowAccuracy}): ${passing}`);
80
+
81
+ if (this.exitCriteriaMet()) {
82
+ parts.push("### Ready\nAll workflows passing. Proceed to PRODUCTION_QC.");
83
+ } else if (created === 0) {
84
+ parts.push("### What to do now\nConvert proven skills into worker LLM workflows.\nFor each skill: write workflow script, write prompts, test vs ground truth, tier-downgrade test.");
85
+ } else {
86
+ const notCreated = this.skillsToDistill.filter((s) => !(s in this.workflowsCreated));
87
+ const notPassing = Object.keys(this.workflowsCreated).filter((s) => !this.workflowsPassing.includes(s));
88
+ let guidance = "### What to do now\n";
89
+ if (notCreated.length) guidance += `Create workflows for: ${notCreated.slice(0, 10).join(", ")}\n`;
90
+ if (notPassing.length) guidance += `Improve accuracy for: ${notPassing.slice(0, 10).join(", ")}\n`;
91
+ parts.push(guidance);
92
+ }
93
+ return parts.join("\n\n");
94
+ }
95
+
96
+ onToolResult(toolName, toolInput, result) {
97
+ if (result.isError) return null;
98
+ const wasReady = this.exitCriteriaMet();
99
+ if (toolName === "workspace_file" && ((toolInput.path || "").includes("workflows/") || (toolInput.path || "").includes("output/"))) {
100
+ this._scanWorkflows();
101
+ }
102
+ if (!wasReady && this.exitCriteriaMet()) {
103
+ return new PipelineEvent({ type: "phase_ready", message: "Distillation complete. Ready for PRODUCTION_QC.", nextPhase: Phase.PRODUCTION_QC });
104
+ }
105
+ return null;
106
+ }
107
+
108
+ exitCriteriaMet() {
109
+ const total = this.skillsToDistill.length;
110
+ if (!total) return false;
111
+ return Object.keys(this.workflowsCreated).length >= total && this.workflowsPassing.length >= total;
112
+ }
113
+ }
@@ -0,0 +1,92 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { Phase, PipelineEvent } from "./index.js";
4
+ import { Pipeline } from "./base.js";
5
+
6
+ export class RuleExtractionPipeline extends Pipeline {
7
+ constructor(workspace) {
8
+ super();
9
+ this._workspace = workspace;
10
+ this.regulationsScanned = false;
11
+ this.rulesExtracted = [];
12
+ this.rulesWithTests = [];
13
+ this.coverageAudited = false;
14
+ this._scanWorkspace();
15
+ }
16
+
17
+ _scanWorkspace() {
18
+ const rulesDir = path.join(this._workspace.cwd, "rules");
19
+ if (fs.existsSync(rulesDir)) {
20
+ const regFiles = fs.readdirSync(rulesDir).filter((f) => !f.endsWith(".json") && fs.statSync(path.join(rulesDir, f)).isFile());
21
+ this.regulationsScanned = regFiles.length > 0;
22
+ }
23
+ this._scanRules();
24
+ this._scanTests();
25
+ this.coverageAudited = fs.existsSync(path.join(this._workspace.cwd, "rules", "coverage_audit.md")) ||
26
+ fs.existsSync(path.join(this._workspace.cwd, "rules", "coverage_audit.json"));
27
+ }
28
+
29
+ _scanRules() {
30
+ this.rulesExtracted = [];
31
+ const catalogPath = path.join(this._workspace.cwd, "rules", "catalog.json");
32
+ if (fs.existsSync(catalogPath)) {
33
+ try {
34
+ const data = JSON.parse(fs.readFileSync(catalogPath, "utf-8"));
35
+ if (Array.isArray(data)) this.rulesExtracted = data.map((r, i) => r.id || `rule_${i}`);
36
+ } catch { /* skip */ }
37
+ }
38
+ const skillsDir = path.join(this._workspace.cwd, "rule_skills");
39
+ if (fs.existsSync(skillsDir)) {
40
+ for (const e of fs.readdirSync(skillsDir, { withFileTypes: true })) {
41
+ if (e.isDirectory() && !e.name.startsWith("__") && !this.rulesExtracted.includes(e.name)) {
42
+ this.rulesExtracted.push(e.name);
43
+ }
44
+ }
45
+ }
46
+ }
47
+
48
+ _scanTests() {
49
+ this.rulesWithTests = [];
50
+ const skillsDir = path.join(this._workspace.cwd, "rule_skills");
51
+ if (!fs.existsSync(skillsDir)) return;
52
+ for (const e of fs.readdirSync(skillsDir, { withFileTypes: true })) {
53
+ if (!e.isDirectory()) continue;
54
+ const testDir = path.join(skillsDir, e.name, "test_cases");
55
+ if (fs.existsSync(testDir) && fs.readdirSync(testDir).length > 0) {
56
+ this.rulesWithTests.push(e.name);
57
+ }
58
+ }
59
+ }
60
+
61
+ describeState() {
62
+ this._scanWorkspace();
63
+ const parts = ["## Current Phase: EXTRACTION"];
64
+ parts.push(`### Progress\n- Regulations scanned: ${this.regulationsScanned ? "yes" : "no"}\n- Rules extracted: ${this.rulesExtracted.length}\n- Rules with tests: ${this.rulesWithTests.length}\n- Coverage audit: ${this.coverageAudited ? "done" : "not yet"}`);
65
+
66
+ if (this.exitCriteriaMet()) {
67
+ parts.push("### Ready\nExtraction complete. Proceed to SKILL_AUTHORING phase.");
68
+ } else if (this.rulesExtracted.length === 0) {
69
+ parts.push("### What to do now\nDecompose regulations into atomic, testable rules.\n- One rule = one pass/fail outcome\n- Work top-down: major areas → chapters → sections → atomic rules\n- Save rules to rules/catalog.json via rule_catalog tool");
70
+ } else if (!this.coverageAudited) {
71
+ parts.push("### What to do now\nRun a coverage audit: which regulation sections are NOT covered? Save to rules/coverage_audit.md");
72
+ }
73
+ return parts.join("\n\n");
74
+ }
75
+
76
+ onToolResult(toolName, toolInput, result) {
77
+ if (result.isError) return null;
78
+ const wasReady = this.exitCriteriaMet();
79
+ if (toolName === "workspace_file" || toolName === "rule_catalog") {
80
+ this._scanWorkspace();
81
+ }
82
+ if (!wasReady && this.exitCriteriaMet()) {
83
+ return new PipelineEvent({ type: "phase_ready", message: "Extraction complete. Ready for SKILL_AUTHORING.", nextPhase: Phase.SKILL_AUTHORING });
84
+ }
85
+ return null;
86
+ }
87
+
88
+ exitCriteriaMet() {
89
+ return this.regulationsScanned && this.rulesExtracted.length > 0 &&
90
+ this.rulesWithTests.length >= Math.max(this.rulesExtracted.length * 0.8, 1) && this.coverageAudited;
91
+ }
92
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Pipeline phases — sequential workflow of the KC Agent methodology.
3
+ */
4
+ export const Phase = Object.freeze({
5
+ BOOTSTRAP: "bootstrap",
6
+ EXTRACTION: "extraction",
7
+ SKILL_AUTHORING: "skill_authoring",
8
+ SKILL_TESTING: "skill_testing",
9
+ DISTILLATION: "distillation",
10
+ PRODUCTION_QC: "production_qc",
11
+ });
12
+
13
+ /**
14
+ * Emitted by pipelines when a milestone or phase transition occurs.
15
+ */
16
+ export class PipelineEvent {
17
+ constructor({ type, message = "", nextPhase = null, data = {} }) {
18
+ this.type = type; // "milestone", "phase_ready", "error"
19
+ this.message = message;
20
+ this.nextPhase = nextPhase;
21
+ this.data = data;
22
+ }
23
+ }
@@ -0,0 +1,163 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import { Phase, PipelineEvent } from "./index.js";
5
+ import { Pipeline } from "./base.js";
6
+
7
+ const REQUIRED_DIRS = ["rules", "samples", "input", "output", "logs", "workflows", "rule_skills"];
8
+
9
+ const DEFAULT_ENV = `# === KC Agent Project Configuration ===
10
+
11
+ # Language: en | zh
12
+ LANGUAGE=en
13
+
14
+ # === Worker LLM API (SiliconFlow) ===
15
+ SILICONFLOW_API_KEY=
16
+ SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1
17
+
18
+ # === Worker LLM Tiers (highest capability to lowest) ===
19
+ TIER1=Pro/zai-org/GLM-5, Pro/moonshotai/Kimi-K2.5
20
+ TIER2=Pro/deepseek-ai/DeepSeek-V3.2, Pro/MiniMaxAI/MiniMax-M2.5, Qwen/Qwen3.5-397B-A17B
21
+ TIER3=Qwen/Qwen3.5-122B-A10B
22
+ TIER4=Qwen/Qwen3.5-35B-A3B
23
+
24
+ # === OCR Model Tiers ===
25
+ OCR_MODEL_TIER1=zai-org/GLM-4.6V
26
+
27
+ # === Quality Thresholds ===
28
+ SKILL_ACCURACY=0.9
29
+ WORKFLOW_ACCURACY=0.9
30
+ MONITOR_FREQUENCY=mid
31
+
32
+ # === Evolution Control ===
33
+ MAX_ITERATIONS=20
34
+ `;
35
+
36
+ export class ProjectInitializer extends Pipeline {
37
+ constructor(workspace) {
38
+ super();
39
+ this._workspace = workspace;
40
+ this.workspaceCreated = false;
41
+ this.configReady = false;
42
+ this.hasRegulations = false;
43
+ this.hasSamples = false;
44
+ this._setupWorkspace();
45
+ }
46
+
47
+ _setupWorkspace() {
48
+ for (const d of REQUIRED_DIRS) {
49
+ fs.mkdirSync(path.join(this._workspace.cwd, d), { recursive: true });
50
+ }
51
+
52
+ const envPath = path.join(this._workspace.cwd, ".env");
53
+ if (!fs.existsSync(envPath)) {
54
+ let envContent = DEFAULT_ENV;
55
+ const gc = this._loadGlobalConfig();
56
+ if (gc.api_key) envContent = envContent.replace("SILICONFLOW_API_KEY=", `SILICONFLOW_API_KEY=${gc.api_key}`);
57
+ if (gc.base_url) envContent = envContent.replace("SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1", `SILICONFLOW_BASE_URL=${gc.base_url}`);
58
+ if (gc.accuracy_threshold) {
59
+ envContent = envContent.replace("SKILL_ACCURACY=0.9", `SKILL_ACCURACY=${gc.accuracy_threshold}`);
60
+ envContent = envContent.replace("WORKFLOW_ACCURACY=0.9", `WORKFLOW_ACCURACY=${gc.accuracy_threshold}`);
61
+ }
62
+ const tiers = gc.tiers || {};
63
+ for (const tk of ["tier1", "tier2", "tier3", "tier4"]) {
64
+ if (tiers[tk]) {
65
+ const tag = tk.toUpperCase();
66
+ envContent = envContent.split("\n").map((l) => l.startsWith(`${tag}=`) ? `${tag}=${tiers[tk]}` : l).join("\n");
67
+ }
68
+ }
69
+ fs.writeFileSync(envPath, envContent, "utf-8");
70
+ }
71
+
72
+ const manifestPath = path.join(this._workspace.cwd, "versions.json");
73
+ if (!fs.existsSync(manifestPath)) {
74
+ fs.writeFileSync(manifestPath, JSON.stringify({ version: "0.1.0", entries: [] }, null, 2), "utf-8");
75
+ }
76
+
77
+ this.workspaceCreated = true;
78
+ this._checkRegulations();
79
+ this._checkSamples();
80
+ this._checkConfig();
81
+ }
82
+
83
+ _checkRegulations() {
84
+ const dir = path.join(this._workspace.cwd, "rules");
85
+ if (!fs.existsSync(dir)) { this.hasRegulations = false; return; }
86
+ this.hasRegulations = fs.readdirSync(dir, { withFileTypes: true }).some((e) => e.isFile());
87
+ }
88
+
89
+ _checkSamples() {
90
+ const dir = path.join(this._workspace.cwd, "samples");
91
+ if (!fs.existsSync(dir)) { this.hasSamples = false; return; }
92
+ this.hasSamples = fs.readdirSync(dir, { withFileTypes: true }).some((e) => e.isFile());
93
+ }
94
+
95
+ _checkConfig() {
96
+ const envPath = path.join(this._workspace.cwd, ".env");
97
+ if (fs.existsSync(envPath)) {
98
+ for (const line of fs.readFileSync(envPath, "utf-8").split("\n")) {
99
+ if (line.startsWith("SILICONFLOW_API_KEY=") && line.split("=")[1].trim()) {
100
+ this.configReady = true; return;
101
+ }
102
+ }
103
+ }
104
+ const gc = this._loadGlobalConfig();
105
+ this.configReady = !!gc.api_key;
106
+ }
107
+
108
+ _loadGlobalConfig() {
109
+ const p = path.join(os.homedir(), ".kc_agent", "config.json");
110
+ if (fs.existsSync(p)) { try { return JSON.parse(fs.readFileSync(p, "utf-8")); } catch { /* skip */ } }
111
+ return {};
112
+ }
113
+
114
+ describeState() {
115
+ const completed = [], pending = [];
116
+ if (this.workspaceCreated) completed.push("Workspace structure created"); else pending.push("Create workspace structure");
117
+ if (this.configReady) completed.push("Configuration ready (API keys set)"); else pending.push("Configure .env (API key needed)");
118
+ if (this.hasRegulations) completed.push("Regulation documents available in rules/"); else pending.push("Regulation documents needed in rules/");
119
+ if (this.hasSamples) completed.push("Sample documents available in samples/"); else pending.push("Sample documents needed in samples/");
120
+
121
+ const parts = ["## Current Phase: BOOTSTRAP"];
122
+ if (completed.length) parts.push("### Completed\n" + completed.map((c) => `- [x] ${c}`).join("\n"));
123
+ if (pending.length) parts.push("### Pending\n" + pending.map((p) => `- [ ] ${p}`).join("\n"));
124
+
125
+ if (this.exitCriteriaMet()) {
126
+ parts.push("### Ready\nAll bootstrap requirements met. Proceed to EXTRACTION phase.");
127
+ } else {
128
+ parts.push(
129
+ "### What to do now\nTalk to the developer user about their verification scenario:\n" +
130
+ "- What documents do they verify?\n- What regulations apply?\n" +
131
+ "- Ask them to provide regulation documents (save to rules/) and sample documents (save to samples/)."
132
+ );
133
+ }
134
+ return parts.join("\n\n");
135
+ }
136
+
137
+ onToolResult(toolName, toolInput, result) {
138
+ if (result.isError) return null;
139
+ const wasReady = this.exitCriteriaMet();
140
+
141
+ if (toolName === "workspace_file") {
142
+ const op = toolInput.operation || "";
143
+ const p = toolInput.path || "";
144
+ if (op === "write") {
145
+ if (p.startsWith("rules/")) this.hasRegulations = true;
146
+ else if (p.startsWith("samples/")) this.hasSamples = true;
147
+ else if (p === ".env") this._checkConfig();
148
+ } else if (op === "list") {
149
+ this._checkRegulations();
150
+ this._checkSamples();
151
+ }
152
+ }
153
+
154
+ if (!wasReady && this.exitCriteriaMet()) {
155
+ return new PipelineEvent({ type: "phase_ready", message: "Bootstrap complete. Ready for EXTRACTION.", nextPhase: Phase.EXTRACTION });
156
+ }
157
+ return null;
158
+ }
159
+
160
+ exitCriteriaMet() {
161
+ return this.workspaceCreated && this.configReady && this.hasRegulations && this.hasSamples;
162
+ }
163
+ }