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.
- package/bin/kc-beta.js +16 -0
- package/package.json +32 -0
- package/src/agent/confidence-scorer.js +120 -0
- package/src/agent/context.js +124 -0
- package/src/agent/corner-case-registry.js +119 -0
- package/src/agent/engine.js +224 -0
- package/src/agent/events.js +27 -0
- package/src/agent/history.js +101 -0
- package/src/agent/llm-client.js +131 -0
- package/src/agent/pipelines/base.js +14 -0
- package/src/agent/pipelines/distillation.js +113 -0
- package/src/agent/pipelines/extraction.js +92 -0
- package/src/agent/pipelines/index.js +23 -0
- package/src/agent/pipelines/initializer.js +163 -0
- package/src/agent/pipelines/production-qc.js +99 -0
- package/src/agent/pipelines/skill-authoring.js +83 -0
- package/src/agent/pipelines/skill-testing.js +111 -0
- package/src/agent/tools/agent-tool.js +100 -0
- package/src/agent/tools/base.js +35 -0
- package/src/agent/tools/dashboard-render.js +146 -0
- package/src/agent/tools/document-parse.js +184 -0
- package/src/agent/tools/document-search.js +111 -0
- package/src/agent/tools/evolution-cycle.js +150 -0
- package/src/agent/tools/qc-sample.js +94 -0
- package/src/agent/tools/registry.js +55 -0
- package/src/agent/tools/rule-catalog.js +113 -0
- package/src/agent/tools/sandbox-exec.js +106 -0
- package/src/agent/tools/tier-downgrade.js +114 -0
- package/src/agent/tools/worker-llm-call.js +109 -0
- package/src/agent/tools/workflow-run.js +138 -0
- package/src/agent/tools/workspace-file.js +122 -0
- package/src/agent/version-manager.js +130 -0
- package/src/agent/workspace.js +82 -0
- package/src/cli/components.js +164 -0
- package/src/cli/index.js +329 -0
- package/src/cli/init.js +80 -0
- package/src/cli/onboard.js +182 -0
- package/src/cli/terminal.js +143 -0
- package/src/config.js +93 -0
- package/template/.env.template +31 -0
- package/template/CLAUDE.md +137 -0
- package/template/Input/.gitkeep +0 -0
- package/template/Output/.gitkeep +0 -0
- package/template/Rules/.gitkeep +0 -0
- package/template/Samples/.gitkeep +0 -0
- package/template/skills/en/meta/compliance-judgment/SKILL.md +114 -0
- package/template/skills/en/meta/compliance-judgment/references/output-format.md +151 -0
- package/template/skills/en/meta/confidence-system/SKILL.md +117 -0
- package/template/skills/en/meta/corner-case-management/SKILL.md +111 -0
- package/template/skills/en/meta/cross-document-verification/SKILL.md +131 -0
- package/template/skills/en/meta/cross-document-verification/references/contradiction-taxonomy.md +73 -0
- package/template/skills/en/meta/data-sensibility/SKILL.md +115 -0
- package/template/skills/en/meta/document-parsing/SKILL.md +108 -0
- package/template/skills/en/meta/document-parsing/references/parser-catalog.md +40 -0
- package/template/skills/en/meta/entity-extraction/SKILL.md +129 -0
- package/template/skills/en/meta/tree-processing/SKILL.md +103 -0
- package/template/skills/en/meta-meta/bootstrap-workspace/SKILL.md +70 -0
- package/template/skills/en/meta-meta/dashboard-reporting/SKILL.md +106 -0
- package/template/skills/en/meta-meta/dashboard-reporting/scripts/generate_dashboard.py +178 -0
- package/template/skills/en/meta-meta/evolution-loop/SKILL.md +210 -0
- package/template/skills/en/meta-meta/evolution-loop/references/convergence-guide.md +62 -0
- package/template/skills/en/meta-meta/quality-control/SKILL.md +138 -0
- package/template/skills/en/meta-meta/quality-control/references/qa-layers.md +92 -0
- package/template/skills/en/meta-meta/quality-control/references/sampling-strategies.md +76 -0
- package/template/skills/en/meta-meta/rule-extraction/SKILL.md +100 -0
- package/template/skills/en/meta-meta/rule-extraction/references/chunking-strategies.md +80 -0
- package/template/skills/en/meta-meta/rule-graph/SKILL.md +118 -0
- package/template/skills/en/meta-meta/skill-authoring/SKILL.md +108 -0
- package/template/skills/en/meta-meta/skill-authoring/references/skill-format-spec.md +78 -0
- package/template/skills/en/meta-meta/skill-to-workflow/SKILL.md +150 -0
- package/template/skills/en/meta-meta/skill-to-workflow/references/worker-llm-catalog.md +50 -0
- package/template/skills/en/meta-meta/task-decomposition/SKILL.md +129 -0
- package/template/skills/en/meta-meta/task-decomposition/references/decision-matrix.md +81 -0
- package/template/skills/en/meta-meta/version-control/SKILL.md +152 -0
- package/template/skills/en/meta-meta/version-control/references/trace-id-spec.md +79 -0
- package/template/skills/en/skill-creator/LICENSE.txt +202 -0
- package/template/skills/en/skill-creator/SKILL.md +479 -0
- package/template/skills/en/skill-creator/agents/analyzer.md +274 -0
- package/template/skills/en/skill-creator/agents/comparator.md +202 -0
- package/template/skills/en/skill-creator/agents/grader.md +223 -0
- package/template/skills/en/skill-creator/assets/eval_review.html +146 -0
- package/template/skills/en/skill-creator/eval-viewer/generate_review.py +471 -0
- package/template/skills/en/skill-creator/eval-viewer/viewer.html +1325 -0
- package/template/skills/en/skill-creator/references/schemas.md +430 -0
- package/template/skills/en/skill-creator/scripts/__init__.py +0 -0
- package/template/skills/en/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/template/skills/en/skill-creator/scripts/generate_report.py +326 -0
- package/template/skills/en/skill-creator/scripts/improve_description.py +248 -0
- package/template/skills/en/skill-creator/scripts/package_skill.py +136 -0
- package/template/skills/en/skill-creator/scripts/quick_validate.py +103 -0
- package/template/skills/en/skill-creator/scripts/run_eval.py +310 -0
- package/template/skills/en/skill-creator/scripts/run_loop.py +332 -0
- package/template/skills/en/skill-creator/scripts/utils.py +47 -0
- package/template/skills/zh/meta/compliance-judgment/SKILL.md +303 -0
- package/template/skills/zh/meta/compliance-judgment/references/output-format.md +151 -0
- package/template/skills/zh/meta/confidence-system/SKILL.md +228 -0
- package/template/skills/zh/meta/corner-case-management/SKILL.md +235 -0
- package/template/skills/zh/meta/cross-document-verification/SKILL.md +241 -0
- package/template/skills/zh/meta/cross-document-verification/references/contradiction-taxonomy.md +73 -0
- package/template/skills/zh/meta/data-sensibility/SKILL.md +235 -0
- package/template/skills/zh/meta/document-parsing/SKILL.md +168 -0
- package/template/skills/zh/meta/document-parsing/references/parser-catalog.md +40 -0
- package/template/skills/zh/meta/entity-extraction/SKILL.md +276 -0
- package/template/skills/zh/meta/tree-processing/SKILL.md +233 -0
- package/template/skills/zh/meta-meta/bootstrap-workspace/SKILL.md +147 -0
- package/template/skills/zh/meta-meta/dashboard-reporting/SKILL.md +281 -0
- package/template/skills/zh/meta-meta/dashboard-reporting/scripts/generate_dashboard.py +178 -0
- package/template/skills/zh/meta-meta/evolution-loop/SKILL.md +302 -0
- package/template/skills/zh/meta-meta/evolution-loop/references/convergence-guide.md +62 -0
- package/template/skills/zh/meta-meta/quality-control/SKILL.md +269 -0
- package/template/skills/zh/meta-meta/quality-control/references/qa-layers.md +92 -0
- package/template/skills/zh/meta-meta/quality-control/references/sampling-strategies.md +76 -0
- package/template/skills/zh/meta-meta/rule-extraction/SKILL.md +208 -0
- package/template/skills/zh/meta-meta/rule-extraction/references/chunking-strategies.md +80 -0
- package/template/skills/zh/meta-meta/rule-graph/SKILL.md +203 -0
- package/template/skills/zh/meta-meta/skill-authoring/SKILL.md +235 -0
- package/template/skills/zh/meta-meta/skill-authoring/references/skill-format-spec.md +78 -0
- package/template/skills/zh/meta-meta/skill-to-workflow/SKILL.md +275 -0
- package/template/skills/zh/meta-meta/skill-to-workflow/references/worker-llm-catalog.md +50 -0
- package/template/skills/zh/meta-meta/task-decomposition/SKILL.md +224 -0
- package/template/skills/zh/meta-meta/task-decomposition/references/decision-matrix.md +81 -0
- package/template/skills/zh/meta-meta/version-control/SKILL.md +284 -0
- package/template/skills/zh/meta-meta/version-control/references/trace-id-spec.md +79 -0
- package/template/skills/zh/skill-creator/LICENSE.txt +202 -0
- package/template/skills/zh/skill-creator/SKILL.md +479 -0
- package/template/skills/zh/skill-creator/agents/analyzer.md +274 -0
- package/template/skills/zh/skill-creator/agents/comparator.md +202 -0
- package/template/skills/zh/skill-creator/agents/grader.md +223 -0
- package/template/skills/zh/skill-creator/assets/eval_review.html +146 -0
- package/template/skills/zh/skill-creator/eval-viewer/generate_review.py +471 -0
- package/template/skills/zh/skill-creator/eval-viewer/viewer.html +1325 -0
- package/template/skills/zh/skill-creator/references/schemas.md +430 -0
- package/template/skills/zh/skill-creator/scripts/__init__.py +0 -0
- package/template/skills/zh/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/template/skills/zh/skill-creator/scripts/generate_report.py +326 -0
- package/template/skills/zh/skill-creator/scripts/improve_description.py +248 -0
- package/template/skills/zh/skill-creator/scripts/package_skill.py +136 -0
- package/template/skills/zh/skill-creator/scripts/quick_validate.py +103 -0
- package/template/skills/zh/skill-creator/scripts/run_eval.py +310 -0
- package/template/skills/zh/skill-creator/scripts/run_loop.py +332 -0
- 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
|
+
}
|