knitbrain 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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/ccr/store.d.ts +57 -0
- package/dist/ccr/store.js +195 -0
- package/dist/ccr/store.js.map +1 -0
- package/dist/dashboard.d.ts +23 -0
- package/dist/dashboard.js +125 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/engine/agents.d.ts +36 -0
- package/dist/engine/agents.js +88 -0
- package/dist/engine/agents.js.map +1 -0
- package/dist/engine/calibration.d.ts +29 -0
- package/dist/engine/calibration.js +72 -0
- package/dist/engine/calibration.js.map +1 -0
- package/dist/engine/feedback.d.ts +30 -0
- package/dist/engine/feedback.js +82 -0
- package/dist/engine/feedback.js.map +1 -0
- package/dist/engine/knowledge.d.ts +22 -0
- package/dist/engine/knowledge.js +154 -0
- package/dist/engine/knowledge.js.map +1 -0
- package/dist/engine/memory.d.ts +35 -0
- package/dist/engine/memory.js +93 -0
- package/dist/engine/memory.js.map +1 -0
- package/dist/engine/meter.d.ts +40 -0
- package/dist/engine/meter.js +61 -0
- package/dist/engine/meter.js.map +1 -0
- package/dist/engine/skills.d.ts +33 -0
- package/dist/engine/skills.js +97 -0
- package/dist/engine/skills.js.map +1 -0
- package/dist/engine/teams.d.ts +28 -0
- package/dist/engine/teams.js +58 -0
- package/dist/engine/teams.js.map +1 -0
- package/dist/engine/workflow.d.ts +18 -0
- package/dist/engine/workflow.js +40 -0
- package/dist/engine/workflow.js.map +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +47 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/pretooluse.d.ts +23 -0
- package/dist/hooks/pretooluse.js +38 -0
- package/dist/hooks/pretooluse.js.map +1 -0
- package/dist/hub/client.d.ts +26 -0
- package/dist/hub/client.js +64 -0
- package/dist/hub/client.js.map +1 -0
- package/dist/hub/server.d.ts +23 -0
- package/dist/hub/server.js +85 -0
- package/dist/hub/server.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/tools.d.ts +40 -0
- package/dist/mcp/tools.js +461 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/measure.d.ts +27 -0
- package/dist/measure.js +25 -0
- package/dist/measure.js.map +1 -0
- package/dist/optimizer/ast.d.ts +19 -0
- package/dist/optimizer/ast.js +189 -0
- package/dist/optimizer/ast.js.map +1 -0
- package/dist/optimizer/code.d.ts +10 -0
- package/dist/optimizer/code.js +231 -0
- package/dist/optimizer/code.js.map +1 -0
- package/dist/optimizer/json.d.ts +9 -0
- package/dist/optimizer/json.js +68 -0
- package/dist/optimizer/json.js.map +1 -0
- package/dist/optimizer/router.d.ts +39 -0
- package/dist/optimizer/router.js +137 -0
- package/dist/optimizer/router.js.map +1 -0
- package/dist/optimizer/text.d.ts +21 -0
- package/dist/optimizer/text.js +88 -0
- package/dist/optimizer/text.js.map +1 -0
- package/dist/optimizer/types.d.ts +13 -0
- package/dist/optimizer/types.js +2 -0
- package/dist/optimizer/types.js.map +1 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.js +44 -0
- package/dist/paths.js.map +1 -0
- package/dist/platforms.d.ts +51 -0
- package/dist/platforms.js +157 -0
- package/dist/platforms.js.map +1 -0
- package/dist/profile.d.ts +3 -0
- package/dist/profile.js +169 -0
- package/dist/profile.js.map +1 -0
- package/dist/proxy/cache-aligner.d.ts +9 -0
- package/dist/proxy/cache-aligner.js +15 -0
- package/dist/proxy/cache-aligner.js.map +1 -0
- package/dist/proxy/index.d.ts +2 -0
- package/dist/proxy/index.js +37 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/optimize-request.d.ts +51 -0
- package/dist/proxy/optimize-request.js +111 -0
- package/dist/proxy/optimize-request.js.map +1 -0
- package/dist/proxy/server.d.ts +31 -0
- package/dist/proxy/server.js +104 -0
- package/dist/proxy/server.js.map +1 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.js +54 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +28 -0
- package/dist/setup.js +96 -0
- package/dist/setup.js.map +1 -0
- package/dist/tokenizer.d.ts +23 -0
- package/dist/tokenizer.js +25 -0
- package/dist/tokenizer.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/** Domains that warrant a mandatory review/verify gate before edits. */
|
|
4
|
+
const SENSITIVE = /\b(auth|security|secret|payment|billing|crypto|db|database|migration|schema)\b/i;
|
|
5
|
+
const DEFAULT_TOOLS = ["Read", "Grep", "Glob", "Edit", "Write"];
|
|
6
|
+
/**
|
|
7
|
+
* Auto-detect candidate domain agents from the knowledge graph: group source
|
|
8
|
+
* files by their directory; each directory with ≥2 files becomes a proposal
|
|
9
|
+
* with sensible, project-specific guardrails. The agent then interviews the
|
|
10
|
+
* user to confirm/edit before create_agent writes them.
|
|
11
|
+
*/
|
|
12
|
+
export function proposeAgents(files) {
|
|
13
|
+
const byDir = new Map();
|
|
14
|
+
for (const f of files) {
|
|
15
|
+
const dir = dirname(f);
|
|
16
|
+
if (dir === "." || dir === "")
|
|
17
|
+
continue;
|
|
18
|
+
const arr = byDir.get(dir);
|
|
19
|
+
if (arr)
|
|
20
|
+
arr.push(f);
|
|
21
|
+
else
|
|
22
|
+
byDir.set(dir, [f]);
|
|
23
|
+
}
|
|
24
|
+
const proposals = [];
|
|
25
|
+
for (const [dir, dirFiles] of byDir) {
|
|
26
|
+
if (dirFiles.length < 2)
|
|
27
|
+
continue;
|
|
28
|
+
const name = dir.split("/").pop();
|
|
29
|
+
const reviewGate = SENSITIVE.test(dir);
|
|
30
|
+
proposals.push({
|
|
31
|
+
name,
|
|
32
|
+
scope: `${dir}/**`,
|
|
33
|
+
files: dirFiles,
|
|
34
|
+
tools: reviewGate ? ["Read", "Grep", "Glob"] : DEFAULT_TOOLS,
|
|
35
|
+
reviewGate,
|
|
36
|
+
contextBudget: 8000,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return proposals.sort((a, b) => a.name.localeCompare(b.name));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Render a project-specific subagent definition with all four guardrails baked
|
|
43
|
+
* in: file/domain scope, allowed-tools allowlist, review gate, context budget.
|
|
44
|
+
*/
|
|
45
|
+
export function generateAgentMarkdown(spec) {
|
|
46
|
+
const tools = (spec.tools ?? DEFAULT_TOOLS).join(", ");
|
|
47
|
+
const scope = spec.scope ?? "(whole project)";
|
|
48
|
+
const budget = spec.contextBudget ?? 8000;
|
|
49
|
+
const description = spec.description ?? `Project agent scoped to ${scope}.`;
|
|
50
|
+
const gate = spec.reviewGate
|
|
51
|
+
? "- **Review gate:** this is a sensitive domain — before any edit, re-verify the exact source (knitbrain_query_dependents + read the real file via knitbrain_retrieve) and have the change reviewed.\n"
|
|
52
|
+
: "";
|
|
53
|
+
return `---
|
|
54
|
+
name: ${spec.name}
|
|
55
|
+
description: ${description}
|
|
56
|
+
tools: ${tools}
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
You are the **${spec.name}** agent for this project.
|
|
60
|
+
|
|
61
|
+
## Guardrails
|
|
62
|
+
- **Scope:** only touch files under \`${scope}\`. Do not edit outside this domain.
|
|
63
|
+
- **Allowed tools:** ${tools}.
|
|
64
|
+
${gate}- **Context budget:** keep your working context under ~${budget} tokens. For large payloads, call \`knitbrain_optimize\` and page originals back with \`knitbrain_retrieve\` only when needed.
|
|
65
|
+
|
|
66
|
+
## How to work
|
|
67
|
+
1. Ground yourself: \`knitbrain_query_imports\` / \`knitbrain_query_dependents\` before editing.
|
|
68
|
+
2. Make the smallest correct change within scope.
|
|
69
|
+
3. Record non-obvious findings with \`knitbrain_record_learning\`.
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
/** Write a generated agent to the project's .claude/agents directory. */
|
|
73
|
+
export function writeAgent(projectRoot, spec) {
|
|
74
|
+
// SECURITY: the name becomes a filename — restrict to a safe slug so it can
|
|
75
|
+
// never escape .claude/agents (no separators, dots, or empty names).
|
|
76
|
+
const safeName = spec.name.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
|
|
77
|
+
if (safeName.length === 0)
|
|
78
|
+
throw new Error(`invalid agent name: ${JSON.stringify(spec.name)}`);
|
|
79
|
+
const dir = join(projectRoot, ".claude", "agents");
|
|
80
|
+
mkdirSync(dir, { recursive: true });
|
|
81
|
+
const path = join(dir, `${safeName}.md`);
|
|
82
|
+
spec = { ...spec, name: safeName };
|
|
83
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
84
|
+
writeFileSync(tmp, generateAgentMarkdown(spec), "utf8");
|
|
85
|
+
renameSync(tmp, path);
|
|
86
|
+
return path;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/engine/agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Tier } from "./workflow.js";
|
|
2
|
+
/**
|
|
3
|
+
* Classifier calibration — the false-positive self-healing loop.
|
|
4
|
+
*
|
|
5
|
+
* When the tier classifier gets it wrong, the agent records a false positive
|
|
6
|
+
* (claimed tier vs what it actually was). After 3 same-direction FPs the
|
|
7
|
+
* scope threshold shifts by 1 (then the counter resets, so the next shift
|
|
8
|
+
* needs 3 fresh votes). Deterministic and per-project, like TOIN: a wrong
|
|
9
|
+
* verdict becomes a training signal, never a permanent annoyance.
|
|
10
|
+
*/
|
|
11
|
+
export interface CalibrationState {
|
|
12
|
+
/** "<claimed>-was-<actual>" → count toward the next shift. */
|
|
13
|
+
fpDirections: Record<string, number>;
|
|
14
|
+
/**
|
|
15
|
+
* Shifts the file-count threshold for "complex". Positive = classifier was
|
|
16
|
+
* over-sensitive (called things complex that weren't) → require more files.
|
|
17
|
+
*/
|
|
18
|
+
scopeAdjust: number;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
}
|
|
21
|
+
export interface Calibration {
|
|
22
|
+
/** Record a wrong classification. Returns the new state (post-shift if tripped). */
|
|
23
|
+
recordFalsePositive(claimed: Tier, actual: Tier): CalibrationState & {
|
|
24
|
+
shifted: boolean;
|
|
25
|
+
};
|
|
26
|
+
get(): CalibrationState;
|
|
27
|
+
reset(): CalibrationState;
|
|
28
|
+
}
|
|
29
|
+
export declare function createCalibration(root: string): Calibration;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/** Same-direction FPs needed before a threshold shift. */
|
|
4
|
+
const ADJUSTMENT_THRESHOLD = 3;
|
|
5
|
+
/** scopeAdjust is clamped — calibration tunes, it must never disable the classifier. */
|
|
6
|
+
const MAX_ADJUST = 2;
|
|
7
|
+
const TIERS = new Set(["inquiry", "trivial", "standard", "complex"]);
|
|
8
|
+
const fresh = () => ({
|
|
9
|
+
fpDirections: {},
|
|
10
|
+
scopeAdjust: 0,
|
|
11
|
+
updatedAt: new Date(0).toISOString(),
|
|
12
|
+
});
|
|
13
|
+
export function createCalibration(root) {
|
|
14
|
+
mkdirSync(root, { recursive: true });
|
|
15
|
+
const path = join(root, "calibration.json");
|
|
16
|
+
// Multiple processes may share this store — re-read disk before every
|
|
17
|
+
// public operation (same lesson as feedback/meter: no stale reads).
|
|
18
|
+
const load = () => {
|
|
19
|
+
if (!existsSync(path))
|
|
20
|
+
return fresh();
|
|
21
|
+
try {
|
|
22
|
+
const p = JSON.parse(readFileSync(path, "utf8"));
|
|
23
|
+
return {
|
|
24
|
+
fpDirections: p.fpDirections && typeof p.fpDirections === "object" ? { ...p.fpDirections } : {},
|
|
25
|
+
scopeAdjust: typeof p.scopeAdjust === "number" ? p.scopeAdjust : 0,
|
|
26
|
+
updatedAt: typeof p.updatedAt === "string" ? p.updatedAt : new Date(0).toISOString(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return fresh();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const save = (state) => {
|
|
34
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
35
|
+
writeFileSync(tmp, JSON.stringify(state, null, 2), "utf8");
|
|
36
|
+
renameSync(tmp, path);
|
|
37
|
+
};
|
|
38
|
+
const clamp = (v) => Math.max(-MAX_ADJUST, Math.min(MAX_ADJUST, v));
|
|
39
|
+
return {
|
|
40
|
+
recordFalsePositive(claimed, actual) {
|
|
41
|
+
if (!TIERS.has(claimed) || !TIERS.has(actual) || claimed === actual) {
|
|
42
|
+
return { ...load(), shifted: false };
|
|
43
|
+
}
|
|
44
|
+
const state = load();
|
|
45
|
+
const direction = `${claimed}-was-${actual}`;
|
|
46
|
+
state.fpDirections[direction] = (state.fpDirections[direction] ?? 0) + 1;
|
|
47
|
+
let shifted = false;
|
|
48
|
+
if (state.fpDirections[direction] >= ADJUSTMENT_THRESHOLD) {
|
|
49
|
+
// Over-sensitive: we said complex, it wasn't → raise the bar.
|
|
50
|
+
if (claimed === "complex")
|
|
51
|
+
state.scopeAdjust = clamp(state.scopeAdjust + 1);
|
|
52
|
+
// Under-sensitive: it WAS complex and we missed it → lower the bar.
|
|
53
|
+
else if (actual === "complex")
|
|
54
|
+
state.scopeAdjust = clamp(state.scopeAdjust - 1);
|
|
55
|
+
// Other directions are counted but don't shift today.
|
|
56
|
+
state.fpDirections[direction] = 0; // next shift needs 3 fresh votes
|
|
57
|
+
shifted = true;
|
|
58
|
+
}
|
|
59
|
+
state.updatedAt = new Date().toISOString();
|
|
60
|
+
save(state);
|
|
61
|
+
return { ...state, shifted };
|
|
62
|
+
},
|
|
63
|
+
get: load,
|
|
64
|
+
reset() {
|
|
65
|
+
const state = fresh();
|
|
66
|
+
state.updatedAt = new Date().toISOString();
|
|
67
|
+
save(state);
|
|
68
|
+
return state;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=calibration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calibration.js","sourceRoot":"","sources":["../../src/engine/calibration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ContentType } from "../optimizer/types.js";
|
|
2
|
+
export interface FeedbackStat {
|
|
3
|
+
kind: ContentType;
|
|
4
|
+
compressions: number;
|
|
5
|
+
retrievals: number;
|
|
6
|
+
/** retrievals / compressions — high means we over-compressed this kind. */
|
|
7
|
+
rate: number;
|
|
8
|
+
/** true once we've backed off (stopped compressing this kind). */
|
|
9
|
+
skipping: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface Feedback {
|
|
12
|
+
/** Record that a payload of `kind` was compressed under `handle`. */
|
|
13
|
+
onCompress(kind: ContentType, handle: string): void;
|
|
14
|
+
/** Record that a stored `handle` was paged back (a vote that the skeleton wasn't enough). */
|
|
15
|
+
onRetrieve(handle: string): void;
|
|
16
|
+
/** Self-tuning verdict: should we STOP compressing this kind (over-retrieved)? */
|
|
17
|
+
shouldSkip(kind: ContentType): boolean;
|
|
18
|
+
stats(): FeedbackStat[];
|
|
19
|
+
}
|
|
20
|
+
export interface FeedbackOptions {
|
|
21
|
+
/** Minimum compressions before the skip verdict can trigger. */
|
|
22
|
+
minSamples?: number;
|
|
23
|
+
/** Retrieval rate above which we back off and stop compressing a kind. */
|
|
24
|
+
maxRate?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* TOIN feedback — deterministic, local self-tuning. Because CCR is lossless, a
|
|
28
|
+
* wrong verdict only costs efficiency (more retrievals), never correctness.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createFeedback(root: string, opts?: FeedbackOptions): Feedback;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const KINDS = ["json", "code", "text", "prose"];
|
|
4
|
+
/**
|
|
5
|
+
* TOIN feedback — deterministic, local self-tuning. Because CCR is lossless, a
|
|
6
|
+
* wrong verdict only costs efficiency (more retrievals), never correctness.
|
|
7
|
+
*/
|
|
8
|
+
export function createFeedback(root, opts = {}) {
|
|
9
|
+
const minSamples = opts.minSamples ?? 8;
|
|
10
|
+
const maxRate = opts.maxRate ?? 0.6;
|
|
11
|
+
mkdirSync(root, { recursive: true });
|
|
12
|
+
const path = join(root, "feedback.json");
|
|
13
|
+
let state = { kinds: {}, handleKind: {} };
|
|
14
|
+
// Multiple processes share this store (MCP server, proxy, dashboard), so
|
|
15
|
+
// every public operation re-reads disk first — a constructed-once instance
|
|
16
|
+
// must never serve stale counters from another process's writes.
|
|
17
|
+
const reload = () => {
|
|
18
|
+
if (!existsSync(path))
|
|
19
|
+
return;
|
|
20
|
+
try {
|
|
21
|
+
state = JSON.parse(readFileSync(path, "utf8"));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
/* keep current in-memory state */
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
reload();
|
|
28
|
+
const bucket = (kind) => {
|
|
29
|
+
const b = state.kinds[kind] ?? { compressions: 0, retrievals: 0 };
|
|
30
|
+
state.kinds[kind] = b;
|
|
31
|
+
return b;
|
|
32
|
+
};
|
|
33
|
+
const save = () => {
|
|
34
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
35
|
+
writeFileSync(tmp, JSON.stringify(state), "utf8");
|
|
36
|
+
renameSync(tmp, path);
|
|
37
|
+
};
|
|
38
|
+
const rate = (kind) => {
|
|
39
|
+
const b = state.kinds[kind];
|
|
40
|
+
if (!b || b.compressions === 0)
|
|
41
|
+
return 0;
|
|
42
|
+
return b.retrievals / b.compressions;
|
|
43
|
+
};
|
|
44
|
+
const skip = (kind) => {
|
|
45
|
+
const b = state.kinds[kind];
|
|
46
|
+
return Boolean(b && b.compressions >= minSamples && rate(kind) > maxRate);
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
onCompress(kind, handle) {
|
|
50
|
+
reload();
|
|
51
|
+
bucket(kind).compressions += 1;
|
|
52
|
+
state.handleKind[handle] = kind;
|
|
53
|
+
save();
|
|
54
|
+
},
|
|
55
|
+
onRetrieve(handle) {
|
|
56
|
+
reload();
|
|
57
|
+
const kind = state.handleKind[handle];
|
|
58
|
+
if (!kind)
|
|
59
|
+
return;
|
|
60
|
+
bucket(kind).retrievals += 1;
|
|
61
|
+
save();
|
|
62
|
+
},
|
|
63
|
+
shouldSkip(kind) {
|
|
64
|
+
reload();
|
|
65
|
+
return skip(kind);
|
|
66
|
+
},
|
|
67
|
+
stats() {
|
|
68
|
+
reload();
|
|
69
|
+
return KINDS.map((kind) => {
|
|
70
|
+
const b = state.kinds[kind] ?? { compressions: 0, retrievals: 0 };
|
|
71
|
+
return {
|
|
72
|
+
kind,
|
|
73
|
+
compressions: b.compressions,
|
|
74
|
+
retrievals: b.retrievals,
|
|
75
|
+
rate: Math.round(rate(kind) * 100) / 100,
|
|
76
|
+
skipping: skip(kind),
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../src/engine/feedback.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface ImportEdge {
|
|
2
|
+
/** The raw module specifier, e.g. "./foo" or "node:fs". */
|
|
3
|
+
from: string;
|
|
4
|
+
/** Imported names (best-effort). */
|
|
5
|
+
names: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface FileNode {
|
|
8
|
+
file: string;
|
|
9
|
+
imports: ImportEdge[];
|
|
10
|
+
exports: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface Knowledge {
|
|
13
|
+
scan(): {
|
|
14
|
+
files: number;
|
|
15
|
+
};
|
|
16
|
+
queryImports(file: string): ImportEdge[] | null;
|
|
17
|
+
queryExports(file: string): string[] | null;
|
|
18
|
+
queryDependents(file: string): string[];
|
|
19
|
+
listFiles(): string[];
|
|
20
|
+
}
|
|
21
|
+
/** Project-scoped import/export graph. Regex-based (TS/JS), dependency-free. */
|
|
22
|
+
export declare function createKnowledge(projectRoot: string, cacheDir: string): Knowledge;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
const SOURCE_EXT = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
|
|
4
|
+
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "coverage", "build", ".next"]);
|
|
5
|
+
function parseImports(src) {
|
|
6
|
+
const edges = [];
|
|
7
|
+
const fromRe = /import\s+(?:type\s+)?([\s\S]*?)\s+from\s*['"]([^'"]+)['"]/g;
|
|
8
|
+
const bareRe = /import\s*['"]([^'"]+)['"]/g;
|
|
9
|
+
const reqRe = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
10
|
+
let m;
|
|
11
|
+
while ((m = fromRe.exec(src)))
|
|
12
|
+
edges.push({ from: m[2], names: parseClause(m[1]) });
|
|
13
|
+
while ((m = bareRe.exec(src)))
|
|
14
|
+
edges.push({ from: m[1], names: [] });
|
|
15
|
+
while ((m = reqRe.exec(src)))
|
|
16
|
+
edges.push({ from: m[1], names: [] });
|
|
17
|
+
return edges;
|
|
18
|
+
}
|
|
19
|
+
function parseClause(clause) {
|
|
20
|
+
const names = [];
|
|
21
|
+
const braced = clause.match(/\{([^}]*)\}/);
|
|
22
|
+
if (braced) {
|
|
23
|
+
for (const part of braced[1].split(",")) {
|
|
24
|
+
const name = part.trim().split(/\s+as\s+/).pop()?.trim();
|
|
25
|
+
if (name)
|
|
26
|
+
names.push(name);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const star = clause.match(/\*\s+as\s+([A-Za-z0-9_$]+)/);
|
|
30
|
+
if (star)
|
|
31
|
+
names.push(star[1]);
|
|
32
|
+
const def = clause.replace(/\{[^}]*\}/, "").replace(/\*\s+as\s+[A-Za-z0-9_$]+/, "").trim().replace(/,$/, "").trim();
|
|
33
|
+
if (def && /^[A-Za-z0-9_$]+$/.test(def))
|
|
34
|
+
names.push(def);
|
|
35
|
+
return names;
|
|
36
|
+
}
|
|
37
|
+
function parseExports(src) {
|
|
38
|
+
const names = new Set();
|
|
39
|
+
const declRe = /export\s+(?:default\s+)?(?:async\s+)?(?:function|class|const|let|var|interface|type|enum)\s+([A-Za-z0-9_$]+)/g;
|
|
40
|
+
const listRe = /export\s*\{([^}]*)\}/g;
|
|
41
|
+
let m;
|
|
42
|
+
while ((m = declRe.exec(src)))
|
|
43
|
+
names.add(m[1]);
|
|
44
|
+
while ((m = listRe.exec(src))) {
|
|
45
|
+
for (const part of m[1].split(",")) {
|
|
46
|
+
const name = part.trim().split(/\s+as\s+/).pop()?.trim();
|
|
47
|
+
if (name)
|
|
48
|
+
names.add(name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (/export\s+default\b/.test(src))
|
|
52
|
+
names.add("default");
|
|
53
|
+
return [...names];
|
|
54
|
+
}
|
|
55
|
+
/** Project-scoped import/export graph. Regex-based (TS/JS), dependency-free. */
|
|
56
|
+
export function createKnowledge(projectRoot, cacheDir) {
|
|
57
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
58
|
+
const cachePath = join(cacheDir, "graph.json");
|
|
59
|
+
const graph = new Map();
|
|
60
|
+
let scanned = false;
|
|
61
|
+
if (existsSync(cachePath)) {
|
|
62
|
+
try {
|
|
63
|
+
const nodes = JSON.parse(readFileSync(cachePath, "utf8"));
|
|
64
|
+
for (const n of nodes)
|
|
65
|
+
graph.set(n.file, n);
|
|
66
|
+
scanned = true;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* rebuild on next query */
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const norm = (file) => relative(projectRoot, resolve(projectRoot, file)).split("\\").join("/");
|
|
73
|
+
function walk(dir, out) {
|
|
74
|
+
for (const entry of readdirSync(dir)) {
|
|
75
|
+
if (SKIP_DIRS.has(entry) || entry.startsWith("."))
|
|
76
|
+
continue;
|
|
77
|
+
const full = join(dir, entry);
|
|
78
|
+
const st = statSync(full);
|
|
79
|
+
if (st.isDirectory())
|
|
80
|
+
walk(full, out);
|
|
81
|
+
else if (SOURCE_EXT.test(entry))
|
|
82
|
+
out.push(full);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function doScan() {
|
|
86
|
+
graph.clear();
|
|
87
|
+
const files = [];
|
|
88
|
+
walk(projectRoot, files);
|
|
89
|
+
for (const full of files) {
|
|
90
|
+
const src = readFileSync(full, "utf8");
|
|
91
|
+
const file = norm(full);
|
|
92
|
+
graph.set(file, { file, imports: parseImports(src), exports: parseExports(src) });
|
|
93
|
+
}
|
|
94
|
+
const tmp = `${cachePath}.${process.pid}.tmp`;
|
|
95
|
+
writeFileSync(tmp, JSON.stringify([...graph.values()]), "utf8");
|
|
96
|
+
renameSync(tmp, cachePath);
|
|
97
|
+
scanned = true;
|
|
98
|
+
return { files: graph.size };
|
|
99
|
+
}
|
|
100
|
+
const ensure = () => {
|
|
101
|
+
if (!scanned || graph.size === 0)
|
|
102
|
+
doScan();
|
|
103
|
+
};
|
|
104
|
+
/** Resolve a relative import specifier from `nodeFile` to a known graph file. */
|
|
105
|
+
function resolveEdge(nodeFile, spec) {
|
|
106
|
+
if (!spec.startsWith("."))
|
|
107
|
+
return null; // external/node module
|
|
108
|
+
const base = resolve(projectRoot, dirname(nodeFile), spec);
|
|
109
|
+
const baseRel = relative(projectRoot, base).split("\\").join("/");
|
|
110
|
+
const candidates = [
|
|
111
|
+
baseRel,
|
|
112
|
+
...["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"].flatMap((e) => [
|
|
113
|
+
`${baseRel}.${e}`,
|
|
114
|
+
`${baseRel}/index.${e}`,
|
|
115
|
+
]),
|
|
116
|
+
// imports often use .js for NodeNext — also try swapping to .ts
|
|
117
|
+
baseRel.replace(/\.js$/, ".ts"),
|
|
118
|
+
];
|
|
119
|
+
for (const c of candidates)
|
|
120
|
+
if (graph.has(c))
|
|
121
|
+
return c;
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
scan: doScan,
|
|
126
|
+
queryImports(file) {
|
|
127
|
+
ensure();
|
|
128
|
+
return graph.get(norm(file))?.imports ?? null;
|
|
129
|
+
},
|
|
130
|
+
queryExports(file) {
|
|
131
|
+
ensure();
|
|
132
|
+
return graph.get(norm(file))?.exports ?? null;
|
|
133
|
+
},
|
|
134
|
+
listFiles() {
|
|
135
|
+
ensure();
|
|
136
|
+
return [...graph.keys()];
|
|
137
|
+
},
|
|
138
|
+
queryDependents(file) {
|
|
139
|
+
ensure();
|
|
140
|
+
const target = norm(file);
|
|
141
|
+
const deps = [];
|
|
142
|
+
for (const node of graph.values()) {
|
|
143
|
+
for (const edge of node.imports) {
|
|
144
|
+
if (resolveEdge(node.file, edge.from) === target) {
|
|
145
|
+
deps.push(node.file);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return deps;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=knowledge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.js","sourceRoot":"","sources":["../../src/engine/knowledge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** A recorded project learning. */
|
|
2
|
+
export interface Learning {
|
|
3
|
+
id: string;
|
|
4
|
+
date: string;
|
|
5
|
+
summary: string;
|
|
6
|
+
lesson: string;
|
|
7
|
+
tags: string[];
|
|
8
|
+
}
|
|
9
|
+
/** A search hit: headline only (call getLearning for the full lesson). */
|
|
10
|
+
export interface LearningHeadline {
|
|
11
|
+
id: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
tags: string[];
|
|
14
|
+
score: number;
|
|
15
|
+
}
|
|
16
|
+
export interface Memory {
|
|
17
|
+
recordLearning(input: {
|
|
18
|
+
summary: string;
|
|
19
|
+
lesson: string;
|
|
20
|
+
tags?: string[];
|
|
21
|
+
}): {
|
|
22
|
+
id: string;
|
|
23
|
+
duplicate: boolean;
|
|
24
|
+
};
|
|
25
|
+
searchLearnings(query: string, limit?: number): LearningHeadline[];
|
|
26
|
+
getLearning(id: string): Learning | undefined;
|
|
27
|
+
listLearnings(): Learning[];
|
|
28
|
+
saveHandoff(state: string): void;
|
|
29
|
+
loadSession(): {
|
|
30
|
+
handoff: string | null;
|
|
31
|
+
topLearnings: LearningHeadline[];
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Per-project, file-backed memory (learnings + handoff). Deterministic, local. */
|
|
35
|
+
export declare function createMemory(root: string): Memory;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const tokenize = (s) => s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1);
|
|
5
|
+
function writeAtomic(path, data) {
|
|
6
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
7
|
+
writeFileSync(tmp, data, "utf8");
|
|
8
|
+
renameSync(tmp, path);
|
|
9
|
+
}
|
|
10
|
+
/** Per-project, file-backed memory (learnings + handoff). Deterministic, local. */
|
|
11
|
+
export function createMemory(root) {
|
|
12
|
+
mkdirSync(root, { recursive: true });
|
|
13
|
+
const learningsPath = join(root, "learnings.json");
|
|
14
|
+
const handoffPath = join(root, "handoff.txt");
|
|
15
|
+
const load = () => {
|
|
16
|
+
if (!existsSync(learningsPath))
|
|
17
|
+
return [];
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(readFileSync(learningsPath, "utf8"));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const headline = (l, score) => ({
|
|
26
|
+
id: l.id,
|
|
27
|
+
summary: l.summary,
|
|
28
|
+
tags: l.tags,
|
|
29
|
+
score,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
recordLearning(input) {
|
|
33
|
+
const all = load();
|
|
34
|
+
const summary = input.summary.trim();
|
|
35
|
+
// Dedup by substring match on summary (mirrors engram behavior).
|
|
36
|
+
const dup = all.find((l) => l.summary.includes(summary) || summary.includes(l.summary));
|
|
37
|
+
if (dup)
|
|
38
|
+
return { id: dup.id, duplicate: true };
|
|
39
|
+
const id = createHash("sha256")
|
|
40
|
+
.update(summary + Date.now())
|
|
41
|
+
.digest("hex")
|
|
42
|
+
.slice(0, 12);
|
|
43
|
+
const learning = {
|
|
44
|
+
id,
|
|
45
|
+
date: new Date().toISOString().slice(0, 10),
|
|
46
|
+
summary,
|
|
47
|
+
lesson: input.lesson,
|
|
48
|
+
tags: input.tags ?? [],
|
|
49
|
+
};
|
|
50
|
+
writeAtomic(learningsPath, JSON.stringify([...all, learning], null, 2));
|
|
51
|
+
return { id, duplicate: false };
|
|
52
|
+
},
|
|
53
|
+
searchLearnings(query, limit = 5) {
|
|
54
|
+
const terms = new Set(tokenize(query));
|
|
55
|
+
if (terms.size === 0)
|
|
56
|
+
return [];
|
|
57
|
+
const scored = load().map((l) => {
|
|
58
|
+
const summaryTerms = new Set(tokenize(l.summary + " " + l.tags.join(" ")));
|
|
59
|
+
const lessonTerms = new Set(tokenize(l.lesson));
|
|
60
|
+
let score = 0;
|
|
61
|
+
for (const t of terms) {
|
|
62
|
+
if (summaryTerms.has(t))
|
|
63
|
+
score += 2; // headline/tag match weighted higher
|
|
64
|
+
else if (lessonTerms.has(t))
|
|
65
|
+
score += 1;
|
|
66
|
+
}
|
|
67
|
+
return headline(l, score);
|
|
68
|
+
});
|
|
69
|
+
return scored
|
|
70
|
+
.filter((h) => h.score > 0)
|
|
71
|
+
.sort((a, b) => b.score - a.score)
|
|
72
|
+
.slice(0, limit);
|
|
73
|
+
},
|
|
74
|
+
getLearning(id) {
|
|
75
|
+
return load().find((l) => l.id === id);
|
|
76
|
+
},
|
|
77
|
+
listLearnings() {
|
|
78
|
+
return load();
|
|
79
|
+
},
|
|
80
|
+
saveHandoff(state) {
|
|
81
|
+
writeAtomic(handoffPath, state);
|
|
82
|
+
},
|
|
83
|
+
loadSession() {
|
|
84
|
+
const handoff = existsSync(handoffPath) ? readFileSync(handoffPath, "utf8") : null;
|
|
85
|
+
const top = load()
|
|
86
|
+
.slice(-5)
|
|
87
|
+
.reverse()
|
|
88
|
+
.map((l) => headline(l, 0));
|
|
89
|
+
return { handoff, topLearnings: top };
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/engine/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AA+BjC,MAAM,QAAQ,GAAG,CAAC,CAAS,EAAY,EAAE,CACvC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAElE,SAAS,WAAW,CAAC,IAAY,EAAE,IAAY;IAC7C,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IACzC,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAE9C,MAAM,IAAI,GAAG,GAAe,EAAE;QAC5B,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAAE,OAAO,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAe,CAAC;QACvE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,CAAW,EAAE,KAAa,EAAoB,EAAE,CAAC,CAAC;QAClE,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,KAAK;KACN,CAAC,CAAC;IAEH,OAAO;QACL,cAAc,CAAC,KAAK;YAClB,MAAM,GAAG,GAAG,IAAI,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACrC,iEAAiE;YACjE,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAClB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAClE,CAAC;YACF,IAAI,GAAG;gBAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YAEhD,MAAM,EAAE,GAAG,UAAU,CAAC,QAAQ,CAAC;iBAC5B,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;iBAC5B,MAAM,CAAC,KAAK,CAAC;iBACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAChB,MAAM,QAAQ,GAAa;gBACzB,EAAE;gBACF,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAC3C,OAAO;gBACP,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;aACvB,CAAC;YACF,WAAW,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,GAAG,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACxE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAClC,CAAC;QAED,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC;YAC9B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;YACvC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9B,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC3E,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;gBAChD,IAAI,KAAK,GAAG,CAAC,CAAC;gBACd,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;oBACtB,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;wBAAE,KAAK,IAAI,CAAC,CAAC,CAAC,qCAAqC;yBACrE,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;wBAAE,KAAK,IAAI,CAAC,CAAC;gBAC1C,CAAC;gBACD,OAAO,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;YACH,OAAO,MAAM;iBACV,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;iBAC1B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;iBACjC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACrB,CAAC;QAED,WAAW,CAAC,EAAE;YACZ,OAAO,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,aAAa;YACX,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,WAAW,CAAC,KAAK;YACf,WAAW,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QAED,WAAW;YACT,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACnF,MAAM,GAAG,GAAG,IAAI,EAAE;iBACf,KAAK,CAAC,CAAC,CAAC,CAAC;iBACT,OAAO,EAAE;iBACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9B,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC;QACxC,CAAC;KACF,CAAC;AACJ,CAAC"}
|