kcode-pi 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/kcode.js +26 -6
- package/package.json +2 -1
- package/src/cli/kcode.ts +219 -0
- package/src/cli/main.ts +10 -0
- package/src/harness/artifacts.ts +94 -0
- package/src/harness/format.ts +30 -0
- package/src/harness/gates.ts +136 -0
- package/src/harness/paths.ts +23 -0
- package/src/harness/state.ts +117 -0
- package/src/harness/types.ts +42 -0
- package/src/knowledge/format.ts +48 -0
- package/src/knowledge/loader.ts +147 -0
- package/src/knowledge/search.ts +118 -0
- package/src/knowledge/types.ts +64 -0
- package/src/official/kingdee-skills.ts +230 -0
- package/src/product/profile.ts +115 -0
- package/src/rules/checker.ts +612 -0
- package/src/tools/build-debug.ts +214 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { extname, join } from "node:path";
|
|
3
|
+
import type { ProductProfile } from "../product/profile.ts";
|
|
4
|
+
import { type CommandSpec, formatCommandResult, resolveWorkspacePath, runCommand } from "../official/kingdee-skills.ts";
|
|
5
|
+
|
|
6
|
+
export interface BuildPlan {
|
|
7
|
+
profile: string;
|
|
8
|
+
command: CommandSpec;
|
|
9
|
+
reason: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DebugFinding {
|
|
13
|
+
severity: "error" | "warning" | "info";
|
|
14
|
+
rule: string;
|
|
15
|
+
message: string;
|
|
16
|
+
evidence: string;
|
|
17
|
+
nextStep: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function planBuild(cwd: string, profile: ProductProfile, target?: string): BuildPlan {
|
|
21
|
+
if (profile.product === "unknown") {
|
|
22
|
+
throw new Error("Product profile is unknown. Set product before running kd_build.");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (profile.platform === "cosmic") return planJavaBuild(cwd, profile, target);
|
|
26
|
+
if (profile.platform === "enterprise-csharp") return planCsharpBuild(cwd, profile, target);
|
|
27
|
+
throw new Error(`No build strategy for ${profile.product}/${profile.platform}/${profile.techStack}.`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function runBuild(plan: BuildPlan, dryRun?: boolean) {
|
|
31
|
+
if (dryRun) {
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: "text" as const, text: formatBuildPlan(plan) }],
|
|
34
|
+
details: { dryRun: true, command: plan.command.display, reason: plan.reason },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const result = await runCommand(plan.command, 10 * 60 * 1000);
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text" as const, text: `${formatBuildPlan(plan)}\n\n${formatCommandResult(result)}` }],
|
|
41
|
+
details: { dryRun: false, command: result.command, exitCode: result.exitCode, reason: plan.reason },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function analyzeDebugText(text: string): DebugFinding[] {
|
|
46
|
+
const findings: DebugFinding[] = [];
|
|
47
|
+
const lines = text.split(/\r?\n/);
|
|
48
|
+
|
|
49
|
+
pushIfMatch(findings, lines, /NullPointerException|Object reference not set/i, {
|
|
50
|
+
severity: "error",
|
|
51
|
+
rule: "null-pointer",
|
|
52
|
+
message: "Detected null pointer failure.",
|
|
53
|
+
nextStep: "Check field metadata, nullable fields, DynamicObject access, and DataSet row getters near the stack frame.",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
pushIfMatch(findings, lines, /ClassNotFoundException|NoClassDefFoundError|FileNotFoundException.*\.jar/i, {
|
|
57
|
+
severity: "error",
|
|
58
|
+
rule: "classpath",
|
|
59
|
+
message: "Detected missing class or jar dependency.",
|
|
60
|
+
nextStep: "Verify module dependency, biz/cuslib deployment, and whether the correct product/version SDK is used.",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
pushIfMatch(findings, lines, /DataSet.*closed|ResultSet.*closed|connection.*closed/i, {
|
|
64
|
+
severity: "error",
|
|
65
|
+
rule: "dataset-scope",
|
|
66
|
+
message: "Detected closed DataSet/connection usage.",
|
|
67
|
+
nextStep: "Keep DataSet processing inside try-with-resources and inside the transaction/resource scope that created it.",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
pushIfMatch(findings, lines, /SQLSyntaxErrorException|PSQLException|syntax error|invalid column|column .* does not exist/i, {
|
|
71
|
+
severity: "error",
|
|
72
|
+
rule: "sql-metadata",
|
|
73
|
+
message: "Detected SQL or metadata mismatch.",
|
|
74
|
+
nextStep: "Use kd_cosmic_metadata with sql=true to verify table names, DB fields, enum values, and dbName/dbKey.",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
pushIfMatch(findings, lines, /deadlock|lock wait timeout|could not serialize access|并发|死锁/i, {
|
|
78
|
+
severity: "error",
|
|
79
|
+
rule: "transaction-lock",
|
|
80
|
+
message: "Detected transaction locking or concurrency failure.",
|
|
81
|
+
nextStep: "Review transaction size, cross-database writes, update ordering, and whether external calls run inside transactions.",
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
pushIfMatch(findings, lines, /OutOfMemoryError|GC overhead|Java heap space|内存溢出/i, {
|
|
85
|
+
severity: "error",
|
|
86
|
+
rule: "memory",
|
|
87
|
+
message: "Detected memory pressure or OOM.",
|
|
88
|
+
nextStep: "Check full-table queries, unbounded collections, deep field paths, and large DataSet/materialized loads.",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
pushIfMatch(findings, lines, /StackOverflowError|propertyChanged/i, {
|
|
92
|
+
severity: "warning",
|
|
93
|
+
rule: "event-recursion",
|
|
94
|
+
message: "Potential recursive event handling.",
|
|
95
|
+
nextStep: "Check propertyChanged/setValue loops and compare old/new values before writing fields.",
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
pushIfMatch(findings, lines, /Gradle|Compilation failed|javac|CS\d{4}|MSB\d{4}|BUILD FAILED|BUILD FAILURE/i, {
|
|
99
|
+
severity: "error",
|
|
100
|
+
rule: "build-error",
|
|
101
|
+
message: "Detected build or compiler error.",
|
|
102
|
+
nextStep: "Use product-specific build output. For Cosmic Java verify SDK signatures; for enterprise C# verify namespaces and references.",
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (findings.length === 0) {
|
|
106
|
+
findings.push({
|
|
107
|
+
severity: "info",
|
|
108
|
+
rule: "no-known-pattern",
|
|
109
|
+
message: "No known Kingdee debug pattern matched.",
|
|
110
|
+
evidence: "",
|
|
111
|
+
nextStep: "Provide fuller logs, stack trace, product/version, target bill/form, and recent code changes.",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return findings;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function formatDebugFindings(findings: DebugFinding[]): string {
|
|
119
|
+
return findings
|
|
120
|
+
.map((finding) =>
|
|
121
|
+
[
|
|
122
|
+
`[${finding.severity}] ${finding.rule}`,
|
|
123
|
+
finding.message,
|
|
124
|
+
finding.evidence ? `Evidence: ${finding.evidence}` : undefined,
|
|
125
|
+
`Next: ${finding.nextStep}`,
|
|
126
|
+
]
|
|
127
|
+
.filter(Boolean)
|
|
128
|
+
.join("\n"),
|
|
129
|
+
)
|
|
130
|
+
.join("\n\n");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function readDebugInput(cwd: string, text?: string, path?: string): { source: string; text: string } {
|
|
134
|
+
if (text) return { source: "inline", text };
|
|
135
|
+
if (!path) throw new Error("Provide either text or path for kd_debug.");
|
|
136
|
+
|
|
137
|
+
const fullPath = resolveWorkspacePath(cwd, path);
|
|
138
|
+
return { source: path, text: readFileSync(fullPath, "utf8") };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function planJavaBuild(cwd: string, profile: ProductProfile, target?: string): BuildPlan {
|
|
142
|
+
const task = target ?? "build";
|
|
143
|
+
const gradlewBat = join(cwd, "gradlew.bat");
|
|
144
|
+
const gradlew = join(cwd, "gradlew");
|
|
145
|
+
const mvnwBat = join(cwd, "mvnw.cmd");
|
|
146
|
+
const mvnw = join(cwd, "mvnw");
|
|
147
|
+
|
|
148
|
+
if (existsSync(gradlewBat)) {
|
|
149
|
+
return buildPlan(profile, gradlewBat, [task], cwd, "Detected Gradle wrapper for Cosmic-family Java project.");
|
|
150
|
+
}
|
|
151
|
+
if (existsSync(gradlew)) {
|
|
152
|
+
return buildPlan(profile, gradlew, [task], cwd, "Detected Gradle wrapper for Cosmic-family Java project.");
|
|
153
|
+
}
|
|
154
|
+
if (existsSync(mvnwBat)) {
|
|
155
|
+
return buildPlan(profile, mvnwBat, [target ?? "test"], cwd, "Detected Maven wrapper for Cosmic-family Java project.");
|
|
156
|
+
}
|
|
157
|
+
if (existsSync(mvnw)) {
|
|
158
|
+
return buildPlan(profile, mvnw, [target ?? "test"], cwd, "Detected Maven wrapper for Cosmic-family Java project.");
|
|
159
|
+
}
|
|
160
|
+
if (existsSync(join(cwd, "build.gradle")) || existsSync(join(cwd, "build.gradle.kts"))) {
|
|
161
|
+
return buildPlan(profile, "gradle", [task], cwd, "Detected Gradle build file; wrapper not found.");
|
|
162
|
+
}
|
|
163
|
+
if (existsSync(join(cwd, "pom.xml"))) {
|
|
164
|
+
return buildPlan(profile, "mvn", [target ?? "test"], cwd, "Detected Maven pom.xml; wrapper not found.");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
throw new Error("No Java build entry found. Expected gradlew, build.gradle, mvnw, or pom.xml in workspace root.");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function planCsharpBuild(cwd: string, profile: ProductProfile, target?: string): BuildPlan {
|
|
171
|
+
const explicit = target ? resolveWorkspacePath(cwd, target) : undefined;
|
|
172
|
+
if (explicit && existsSync(explicit)) {
|
|
173
|
+
return buildPlan(profile, "dotnet", ["build", explicit], cwd, "Using explicit C# project or solution target.");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const candidates = readdirSync(cwd).filter((entry) => [".sln", ".csproj"].includes(extname(entry).toLowerCase()));
|
|
177
|
+
if (candidates.length > 0) {
|
|
178
|
+
return buildPlan(profile, "dotnet", ["build", join(cwd, candidates[0])], cwd, `Detected ${candidates[0]} for enterprise C# project.`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
throw new Error("No C# build entry found. Expected .sln or .csproj in workspace root, or pass target path.");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function buildPlan(profile: ProductProfile, executable: string, args: string[], cwd: string, reason: string): BuildPlan {
|
|
185
|
+
return {
|
|
186
|
+
profile: `${profile.product}/${profile.platform}/${profile.techStack}`,
|
|
187
|
+
command: {
|
|
188
|
+
executable,
|
|
189
|
+
args,
|
|
190
|
+
cwd,
|
|
191
|
+
display: [executable, ...args].map(formatArg).join(" "),
|
|
192
|
+
},
|
|
193
|
+
reason,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function formatBuildPlan(plan: BuildPlan): string {
|
|
198
|
+
return [`Product: ${plan.profile}`, `Reason: ${plan.reason}`, `Command: ${plan.command.display}`].join("\n");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function formatArg(value: string): string {
|
|
202
|
+
return /[\s"'&|<>]/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function pushIfMatch(
|
|
206
|
+
findings: DebugFinding[],
|
|
207
|
+
lines: string[],
|
|
208
|
+
pattern: RegExp,
|
|
209
|
+
template: Omit<DebugFinding, "evidence">,
|
|
210
|
+
): void {
|
|
211
|
+
const line = lines.find((candidate) => pattern.test(candidate));
|
|
212
|
+
if (!line) return;
|
|
213
|
+
findings.push({ ...template, evidence: line.trim().slice(0, 500) });
|
|
214
|
+
}
|