codetrap 0.1.5 → 0.1.7
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/README.md +52 -1
- package/docs/installation.md +21 -3
- package/package.json +3 -1
- package/scripts/dogfood-eval.ts +53 -0
- package/src/commands/workflow.ts +308 -7
- package/src/db/connection.ts +7 -7
- package/src/domain/session.ts +119 -0
- package/src/index.ts +8 -0
- package/src/lib/command-requests.ts +178 -0
- package/src/lib/doctor.ts +62 -2
- package/src/lib/embed-output.ts +26 -0
- package/src/lib/scope-context.ts +7 -7
- package/src/lib/scope.ts +4 -4
- package/src/lib/search-eval.ts +505 -0
- package/src/lib/session-capture.ts +96 -0
- package/src/lib/session-codec.ts +261 -0
- package/src/lib/session-conflicts.ts +104 -0
- package/src/lib/session-operations.ts +251 -0
- package/src/lib/session-store.ts +611 -0
- package/src/lib/store.ts +7 -3
- package/src/lib/trap-quality.ts +111 -0
- package/src/lib/trap-scope-match.ts +1 -1
- package/src/web/client-script.ts +1168 -0
- package/src/web/client-text.ts +335 -0
- package/src/web/project-registry.ts +106 -0
- package/src/web/server.ts +500 -0
- package/src/web/static.ts +528 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { CandidateQuality, CandidateTrap } from "../domain/session";
|
|
2
|
+
|
|
3
|
+
type CandidateDraft = Pick<CandidateTrap, "trap" | "evidence">;
|
|
4
|
+
|
|
5
|
+
export function scoreCandidateTrap(candidate: CandidateDraft): { score: number; quality: CandidateQuality } {
|
|
6
|
+
const trap = candidate.trap;
|
|
7
|
+
const evidenceCount = candidate.evidence.length;
|
|
8
|
+
const hasClearTrigger = hasMeaningfulText(trap.context) && hasTriggerLanguage(trap.context);
|
|
9
|
+
const hasClearMistake = hasMeaningfulText(trap.mistake);
|
|
10
|
+
const hasActionableFix = hasMeaningfulText(trap.fix) && hasActionLanguage(trap.fix);
|
|
11
|
+
const futureReuseLikely = hasFutureReuseSignal(candidate);
|
|
12
|
+
const properScope = trap.scope === "global" || hasSpecificScope(candidate);
|
|
13
|
+
const notTooBroad = !isTooBroad(candidate);
|
|
14
|
+
|
|
15
|
+
const warnings: string[] = [];
|
|
16
|
+
if (!hasClearTrigger) warnings.push("context does not clearly describe when the trap applies");
|
|
17
|
+
if (!hasClearMistake) warnings.push("mistake is not specific enough");
|
|
18
|
+
if (!hasActionableFix) warnings.push("fix is not actionable enough");
|
|
19
|
+
if (!futureReuseLikely) warnings.push("future reuse is unclear");
|
|
20
|
+
if (!properScope) warnings.push("scope is too loose for a project trap");
|
|
21
|
+
if (!notTooBroad) warnings.push("candidate reads like a broad reminder rather than a durable trap");
|
|
22
|
+
if (evidenceCount === 0) warnings.push("candidate has no evidence");
|
|
23
|
+
|
|
24
|
+
const score =
|
|
25
|
+
(hasClearTrigger ? 0.2 : 0) +
|
|
26
|
+
(hasClearMistake ? 0.2 : 0) +
|
|
27
|
+
(hasActionableFix ? 0.2 : 0) +
|
|
28
|
+
(futureReuseLikely ? 0.15 : 0) +
|
|
29
|
+
(properScope ? 0.1 : 0) +
|
|
30
|
+
(evidenceCount > 0 ? 0.1 : 0) +
|
|
31
|
+
(notTooBroad ? 0.05 : 0);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
score: roundScore(score),
|
|
35
|
+
quality: {
|
|
36
|
+
has_clear_trigger: hasClearTrigger,
|
|
37
|
+
has_clear_mistake: hasClearMistake,
|
|
38
|
+
has_actionable_fix: hasActionableFix,
|
|
39
|
+
not_too_broad: notTooBroad,
|
|
40
|
+
future_reuse_likely: futureReuseLikely,
|
|
41
|
+
proper_scope: properScope,
|
|
42
|
+
evidence_count: evidenceCount,
|
|
43
|
+
conflict_checked: false,
|
|
44
|
+
conflict_status: "none",
|
|
45
|
+
staleness_risk: "low",
|
|
46
|
+
suggested_action: suggestedAction(score),
|
|
47
|
+
warnings,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function hasMeaningfulText(value: string | undefined): boolean {
|
|
53
|
+
return typeof value === "string" && value.replace(/\s+/g, " ").trim().length >= 24;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function hasTriggerLanguage(value: string): boolean {
|
|
57
|
+
const normalized = value.toLowerCase();
|
|
58
|
+
return /\bwhen\b|\bwhile\b|\bduring\b|\bif\b/.test(normalized) || /当|如果|处理|实现|修改/.test(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function hasActionLanguage(value: string): boolean {
|
|
62
|
+
const normalized = value.toLowerCase();
|
|
63
|
+
return /\buse\b|\bavoid\b|\bprefer\b|\bkeep\b|\bcheck\b|\bcall\b|\bwrite\b|\badd\b|\bverify\b/.test(normalized)
|
|
64
|
+
|| /使用|避免|优先|检查|验证|改用|保留|调用/.test(value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hasFutureReuseSignal(candidate: CandidateDraft): boolean {
|
|
68
|
+
const tags = candidate.trap.tags ?? [];
|
|
69
|
+
return tags.length > 0
|
|
70
|
+
|| (candidate.trap.path_globs?.length ?? 0) > 0
|
|
71
|
+
|| Boolean(candidate.trap.module)
|
|
72
|
+
|| Boolean(candidate.trap.owner);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hasSpecificScope(candidate: CandidateDraft): boolean {
|
|
76
|
+
return (candidate.trap.path_globs?.length ?? 0) > 0
|
|
77
|
+
|| Boolean(candidate.trap.module)
|
|
78
|
+
|| Boolean(candidate.trap.owner)
|
|
79
|
+
|| candidate.evidence.some((evidence) => (evidence.related_files?.length ?? 0) > 0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isTooBroad(candidate: CandidateDraft): boolean {
|
|
83
|
+
const combined = [
|
|
84
|
+
candidate.trap.title,
|
|
85
|
+
candidate.trap.context,
|
|
86
|
+
candidate.trap.mistake,
|
|
87
|
+
candidate.trap.fix,
|
|
88
|
+
].join(" ").toLowerCase();
|
|
89
|
+
|
|
90
|
+
const broadPhrases = [
|
|
91
|
+
"read the docs",
|
|
92
|
+
"write better code",
|
|
93
|
+
"be careful",
|
|
94
|
+
"check everything",
|
|
95
|
+
"always test",
|
|
96
|
+
"先看文档",
|
|
97
|
+
"小心",
|
|
98
|
+
"写好代码",
|
|
99
|
+
];
|
|
100
|
+
return broadPhrases.some((phrase) => combined.includes(phrase));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function roundScore(value: number): number {
|
|
104
|
+
return Math.round(value * 100) / 100;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function suggestedAction(score: number): CandidateQuality["suggested_action"] {
|
|
108
|
+
if (score >= 0.8) return "accept";
|
|
109
|
+
if (score >= 0.6) return "edit";
|
|
110
|
+
return "reject";
|
|
111
|
+
}
|
|
@@ -46,7 +46,7 @@ export function normalizePath(path: string): string {
|
|
|
46
46
|
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
function globMatchesPath(glob: string, path: string): boolean {
|
|
49
|
+
export function globMatchesPath(glob: string, path: string): boolean {
|
|
50
50
|
const normalizedGlob = normalizePath(glob);
|
|
51
51
|
return new RegExp(`^${globToRegExp(normalizedGlob)}$`).test(path);
|
|
52
52
|
}
|