@smartledger.technology/openai-claw 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 +134 -0
- package/dist/agent.js +262 -0
- package/dist/agent.js.map +1 -0
- package/dist/autopr/index.js +127 -0
- package/dist/autopr/index.js.map +1 -0
- package/dist/client.js +199 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/index.js +624 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/config.js +71 -0
- package/dist/config.js.map +1 -0
- package/dist/cost.js +97 -0
- package/dist/cost.js.map +1 -0
- package/dist/eval/cli.js +32 -0
- package/dist/eval/cli.js.map +1 -0
- package/dist/eval/index.js +134 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/hooks/index.js +69 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.js +246 -0
- package/dist/index.js.map +1 -0
- package/dist/input.js +96 -0
- package/dist/input.js.map +1 -0
- package/dist/mcp/index.js +135 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/memory/compaction.js +70 -0
- package/dist/memory/compaction.js.map +1 -0
- package/dist/memory/index.js +56 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/notifications/index.js +80 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/permissions/index.js +104 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/planmode.js +12 -0
- package/dist/planmode.js.map +1 -0
- package/dist/plugins/index.js +239 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/prompts/system.js +148 -0
- package/dist/prompts/system.js.map +1 -0
- package/dist/rag/index.js +158 -0
- package/dist/rag/index.js.map +1 -0
- package/dist/self-review/index.js +157 -0
- package/dist/self-review/index.js.map +1 -0
- package/dist/session.js +113 -0
- package/dist/session.js.map +1 -0
- package/dist/skills/index.js +39 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/subagent.js +128 -0
- package/dist/subagent.js.map +1 -0
- package/dist/subagents/index.js +61 -0
- package/dist/subagents/index.js.map +1 -0
- package/dist/tools/bash.js +94 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.js +64 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.js +34 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.js +87 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.js +44 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/ls.js +39 -0
- package/dist/tools/ls.js.map +1 -0
- package/dist/tools/read.js +126 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/semantic.js +40 -0
- package/dist/tools/semantic.js.map +1 -0
- package/dist/tools/shell.js +131 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/tools/task.js +91 -0
- package/dist/tools/task.js.map +1 -0
- package/dist/tools/todo.js +64 -0
- package/dist/tools/todo.js.map +1 -0
- package/dist/tools/types.js +7 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/webfetch.js +62 -0
- package/dist/tools/webfetch.js.map +1 -0
- package/dist/tools/websearch.js +82 -0
- package/dist/tools/websearch.js.map +1 -0
- package/dist/tools/write.js +39 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/ui/repl.js +203 -0
- package/dist/ui/repl.js.map +1 -0
- package/dist/ui/tui/App.js +234 -0
- package/dist/ui/tui/App.js.map +1 -0
- package/dist/ui/tui/Diff.js +38 -0
- package/dist/ui/tui/Diff.js.map +1 -0
- package/dist/ui/tui/MessageView.js +50 -0
- package/dist/ui/tui/MessageView.js.map +1 -0
- package/dist/ui/tui/PermissionPrompt.js +47 -0
- package/dist/ui/tui/PermissionPrompt.js.map +1 -0
- package/dist/ui/tui/SlashSuggest.js +36 -0
- package/dist/ui/tui/SlashSuggest.js.map +1 -0
- package/dist/ui/tui/StatusBar.js +31 -0
- package/dist/ui/tui/StatusBar.js.map +1 -0
- package/dist/ui/tui/highlight.js +66 -0
- package/dist/ui/tui/highlight.js.map +1 -0
- package/dist/ui/tui/index.js +12 -0
- package/dist/ui/tui/index.js.map +1 -0
- package/dist/ui/tui/markdown.js +46 -0
- package/dist/ui/tui/markdown.js.map +1 -0
- package/dist/ui/tui/types.js +2 -0
- package/dist/ui/tui/types.js.map +1 -0
- package/dist/web/index.js +195 -0
- package/dist/web/index.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import OpenAI from "openai";
|
|
4
|
+
const EMBED_MODEL = "text-embedding-3-small";
|
|
5
|
+
const CHUNK_CHARS = 4000;
|
|
6
|
+
const CHUNK_OVERLAP = 400;
|
|
7
|
+
const MAX_FILE_BYTES = 256 * 1024;
|
|
8
|
+
const IGNORE_DIRS = new Set(["node_modules", ".git", "dist", "build", ".next", "coverage", ".cache"]);
|
|
9
|
+
const EXT_ALLOW = new Set([
|
|
10
|
+
".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
|
|
11
|
+
".py", ".rb", ".go", ".rs", ".java", ".kt", ".swift",
|
|
12
|
+
".c", ".h", ".cpp", ".hpp", ".cc", ".cs",
|
|
13
|
+
".md", ".txt", ".json", ".yaml", ".yml", ".toml",
|
|
14
|
+
".sh", ".bash", ".zsh", ".fish",
|
|
15
|
+
".html", ".css", ".scss", ".sass",
|
|
16
|
+
".sql", ".graphql", ".proto",
|
|
17
|
+
]);
|
|
18
|
+
function indexFile(config) {
|
|
19
|
+
return path.join(config.projectDir, "index.json");
|
|
20
|
+
}
|
|
21
|
+
export function loadIndex(config) {
|
|
22
|
+
const f = indexFile(config);
|
|
23
|
+
if (!fs.existsSync(f))
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(fs.readFileSync(f, "utf8"));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function saveIndex(config, idx) {
|
|
33
|
+
fs.writeFileSync(indexFile(config), JSON.stringify(idx));
|
|
34
|
+
}
|
|
35
|
+
function* walkFiles(dir, root) {
|
|
36
|
+
let entries;
|
|
37
|
+
try {
|
|
38
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const e of entries) {
|
|
44
|
+
if (e.name.startsWith(".") && IGNORE_DIRS.has(e.name))
|
|
45
|
+
continue;
|
|
46
|
+
const full = path.join(dir, e.name);
|
|
47
|
+
if (e.isDirectory()) {
|
|
48
|
+
if (IGNORE_DIRS.has(e.name))
|
|
49
|
+
continue;
|
|
50
|
+
yield* walkFiles(full, root);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!e.isFile())
|
|
54
|
+
continue;
|
|
55
|
+
const ext = path.extname(e.name).toLowerCase();
|
|
56
|
+
if (!EXT_ALLOW.has(ext))
|
|
57
|
+
continue;
|
|
58
|
+
yield full;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function chunkText(text) {
|
|
62
|
+
if (text.length <= CHUNK_CHARS)
|
|
63
|
+
return [text];
|
|
64
|
+
const out = [];
|
|
65
|
+
let pos = 0;
|
|
66
|
+
while (pos < text.length) {
|
|
67
|
+
const end = Math.min(text.length, pos + CHUNK_CHARS);
|
|
68
|
+
out.push(text.slice(pos, end));
|
|
69
|
+
if (end >= text.length)
|
|
70
|
+
break;
|
|
71
|
+
pos = end - CHUNK_OVERLAP;
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build (or rebuild) the project's semantic index. Calls OpenAI in batches.
|
|
77
|
+
* Returns the new index plus a small summary.
|
|
78
|
+
*/
|
|
79
|
+
export async function buildIndex(config, onProgress) {
|
|
80
|
+
const client = new OpenAI({ apiKey: config.apiKey, baseURL: config.baseURL });
|
|
81
|
+
const files = [];
|
|
82
|
+
for (const f of walkFiles(config.workdir, config.workdir)) {
|
|
83
|
+
try {
|
|
84
|
+
const stat = fs.statSync(f);
|
|
85
|
+
if (stat.size > MAX_FILE_BYTES)
|
|
86
|
+
continue;
|
|
87
|
+
files.push(f);
|
|
88
|
+
}
|
|
89
|
+
catch { }
|
|
90
|
+
}
|
|
91
|
+
onProgress?.(`scanning ${files.length} file(s)…`);
|
|
92
|
+
const pending = [];
|
|
93
|
+
for (const f of files) {
|
|
94
|
+
try {
|
|
95
|
+
const text = fs.readFileSync(f, "utf8");
|
|
96
|
+
if (!text.trim())
|
|
97
|
+
continue;
|
|
98
|
+
const rel = path.relative(config.workdir, f);
|
|
99
|
+
const parts = chunkText(text);
|
|
100
|
+
parts.forEach((p, i) => pending.push({ file: rel, chunkIndex: i, text: p }));
|
|
101
|
+
}
|
|
102
|
+
catch { }
|
|
103
|
+
}
|
|
104
|
+
onProgress?.(`embedding ${pending.length} chunk(s)…`);
|
|
105
|
+
const BATCH = 64;
|
|
106
|
+
const chunks = [];
|
|
107
|
+
for (let i = 0; i < pending.length; i += BATCH) {
|
|
108
|
+
const slice = pending.slice(i, i + BATCH);
|
|
109
|
+
const res = await client.embeddings.create({
|
|
110
|
+
model: EMBED_MODEL,
|
|
111
|
+
input: slice.map((p) => p.text),
|
|
112
|
+
});
|
|
113
|
+
for (let j = 0; j < slice.length; j++) {
|
|
114
|
+
chunks.push({
|
|
115
|
+
file: slice[j].file,
|
|
116
|
+
chunkIndex: slice[j].chunkIndex,
|
|
117
|
+
text: slice[j].text,
|
|
118
|
+
embedding: res.data[j].embedding,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
onProgress?.(`embedded ${Math.min(i + BATCH, pending.length)}/${pending.length}`);
|
|
122
|
+
}
|
|
123
|
+
const idx = {
|
|
124
|
+
workdir: config.workdir,
|
|
125
|
+
model: EMBED_MODEL,
|
|
126
|
+
builtAt: new Date().toISOString(),
|
|
127
|
+
chunks,
|
|
128
|
+
};
|
|
129
|
+
saveIndex(config, idx);
|
|
130
|
+
return { index: idx, filesIndexed: files.length, chunks: chunks.length };
|
|
131
|
+
}
|
|
132
|
+
function cosine(a, b) {
|
|
133
|
+
let dot = 0, aMag = 0, bMag = 0;
|
|
134
|
+
for (let i = 0; i < a.length; i++) {
|
|
135
|
+
dot += a[i] * b[i];
|
|
136
|
+
aMag += a[i] * a[i];
|
|
137
|
+
bMag += b[i] * b[i];
|
|
138
|
+
}
|
|
139
|
+
return dot / (Math.sqrt(aMag) * Math.sqrt(bMag) + 1e-10);
|
|
140
|
+
}
|
|
141
|
+
export async function semanticSearch(config, query, k) {
|
|
142
|
+
const idx = loadIndex(config);
|
|
143
|
+
if (!idx || idx.chunks.length === 0) {
|
|
144
|
+
throw new Error("No semantic index. Run /index to build one.");
|
|
145
|
+
}
|
|
146
|
+
const client = new OpenAI({ apiKey: config.apiKey, baseURL: config.baseURL });
|
|
147
|
+
const res = await client.embeddings.create({ model: idx.model, input: [query] });
|
|
148
|
+
const qvec = res.data[0].embedding;
|
|
149
|
+
const scored = idx.chunks.map((c) => ({
|
|
150
|
+
file: c.file,
|
|
151
|
+
chunkIndex: c.chunkIndex,
|
|
152
|
+
score: cosine(qvec, c.embedding),
|
|
153
|
+
text: c.text,
|
|
154
|
+
}));
|
|
155
|
+
scored.sort((a, b) => b.score - a.score);
|
|
156
|
+
return scored.slice(0, k);
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rag/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { listSessions, loadSession } from "../session.js";
|
|
3
|
+
import { listMemories, writeMemory } from "../memory/index.js";
|
|
4
|
+
import { OpenAIClient } from "../client.js";
|
|
5
|
+
// Phrases that typically follow a user correction or course-redirect.
|
|
6
|
+
const CORRECTION_PATTERNS = [
|
|
7
|
+
/\bdon't\b/i,
|
|
8
|
+
/\bdo not\b/i,
|
|
9
|
+
/\bstop\b/i,
|
|
10
|
+
/\bnot like that\b/i,
|
|
11
|
+
/\binstead\b/i,
|
|
12
|
+
/\bno,? that's\b/i,
|
|
13
|
+
/\bthat's wrong\b/i,
|
|
14
|
+
/\bplease use\b/i,
|
|
15
|
+
/\bprefer\b/i,
|
|
16
|
+
];
|
|
17
|
+
function scanSessions(config, n) {
|
|
18
|
+
const sessions = listSessions(config).slice(0, n);
|
|
19
|
+
const findings = [];
|
|
20
|
+
const toolErrorCounter = new Map();
|
|
21
|
+
for (const s of sessions) {
|
|
22
|
+
const data = loadSession(config, s.id);
|
|
23
|
+
if (!data)
|
|
24
|
+
continue;
|
|
25
|
+
for (const m of data.messages) {
|
|
26
|
+
if (m.role === "user") {
|
|
27
|
+
const text = typeof m.content === "string" ? m.content : Array.isArray(m.content)
|
|
28
|
+
? m.content.map((p) => (p.type === "text" ? p.text : "")).join(" ")
|
|
29
|
+
: "";
|
|
30
|
+
if (!text)
|
|
31
|
+
continue;
|
|
32
|
+
if (CORRECTION_PATTERNS.some((re) => re.test(text))) {
|
|
33
|
+
findings.push({ type: "correction", text: text.slice(0, 400), fromSession: s.id });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (m.role === "tool" && typeof m.content === "string" && /\berror\b|\bfailed\b|\bnot found\b/i.test(m.content)) {
|
|
37
|
+
const key = `${m.name ?? "?"}:${m.content.split("\n")[0].slice(0, 80)}`;
|
|
38
|
+
toolErrorCounter.set(key, (toolErrorCounter.get(key) ?? 0) + 1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const [key, count] of toolErrorCounter) {
|
|
43
|
+
if (count >= 2) {
|
|
44
|
+
findings.push({ type: "tool_error", text: `${key} (x${count})`, fromSession: "(multiple)" });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { findings, sessionsScanned: sessions.length };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Ask the model to propose memory entries that, if applied, would prevent the
|
|
51
|
+
* patterns observed across recent sessions. We deliberately do not write
|
|
52
|
+
* anything to disk here — the caller surfaces the proposals to the user.
|
|
53
|
+
*/
|
|
54
|
+
export async function runSelfReview(config, opts = {}) {
|
|
55
|
+
const n = opts.sessionLimit ?? 20;
|
|
56
|
+
const { findings, sessionsScanned } = scanSessions(config, n);
|
|
57
|
+
if (findings.length === 0) {
|
|
58
|
+
return {
|
|
59
|
+
sessionsScanned,
|
|
60
|
+
signals: { correctionPhrases: 0, repeatedToolErrors: 0 },
|
|
61
|
+
proposals: [],
|
|
62
|
+
raw: "(no signals found across recent sessions)",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const existing = listMemories(config).map((m) => `- ${m.name} (${m.type}): ${m.description}`).join("\n") || "(none)";
|
|
66
|
+
const findingsBlock = findings.map((f, i) => `${i + 1}. [${f.type}] ${f.text}`).join("\n");
|
|
67
|
+
const sys = {
|
|
68
|
+
role: "system",
|
|
69
|
+
content: `You audit a coding assistant's recent sessions and propose persistent memory updates that would prevent the same friction next time. Output STRICT JSON in this shape:
|
|
70
|
+
|
|
71
|
+
{
|
|
72
|
+
"summary": "<one-paragraph synthesis of what you saw>",
|
|
73
|
+
"proposals": [
|
|
74
|
+
{
|
|
75
|
+
"type": "user" | "feedback" | "project" | "reference",
|
|
76
|
+
"name": "<kebab-case slug>",
|
|
77
|
+
"description": "<one-line description>",
|
|
78
|
+
"body": "<the memory body — for feedback include **Why:** and **How to apply:** lines>"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Rules:
|
|
84
|
+
- Only propose what is NOT already covered by the existing memories.
|
|
85
|
+
- Prefer FEEDBACK memories for user-correction patterns; PROJECT memories for ongoing-work facts.
|
|
86
|
+
- 0 to 5 proposals; quality over quantity. If no proposals are warranted, return an empty array.`,
|
|
87
|
+
};
|
|
88
|
+
const usr = {
|
|
89
|
+
role: "user",
|
|
90
|
+
content: `Existing memories:
|
|
91
|
+
${existing}
|
|
92
|
+
|
|
93
|
+
Findings across the last ${sessionsScanned} session(s):
|
|
94
|
+
${findingsBlock}
|
|
95
|
+
|
|
96
|
+
Return JSON.`,
|
|
97
|
+
};
|
|
98
|
+
const client = new OpenAIClient(config);
|
|
99
|
+
const res = await client.complete([sys, usr], [], { modelRole: "reasoning" });
|
|
100
|
+
const raw = res.content ?? "";
|
|
101
|
+
let proposals = [];
|
|
102
|
+
try {
|
|
103
|
+
const json = JSON.parse(extractJson(raw));
|
|
104
|
+
if (Array.isArray(json.proposals)) {
|
|
105
|
+
for (const p of json.proposals) {
|
|
106
|
+
if (p && typeof p.name === "string" && typeof p.body === "string") {
|
|
107
|
+
proposals.push({
|
|
108
|
+
kind: "memory",
|
|
109
|
+
type: p.type ?? "feedback",
|
|
110
|
+
name: p.name,
|
|
111
|
+
description: p.description ?? "",
|
|
112
|
+
body: p.body,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// best-effort — leave proposals empty
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
sessionsScanned,
|
|
123
|
+
signals: {
|
|
124
|
+
correctionPhrases: findings.filter((f) => f.type === "correction").length,
|
|
125
|
+
repeatedToolErrors: findings.filter((f) => f.type === "tool_error").length,
|
|
126
|
+
},
|
|
127
|
+
proposals,
|
|
128
|
+
raw,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** Apply an approved proposal by writing it to the memory store. */
|
|
132
|
+
export function applyProposal(config, p) {
|
|
133
|
+
return require_writeMemory_path(writeMemory)(config, {
|
|
134
|
+
name: p.name,
|
|
135
|
+
description: p.description,
|
|
136
|
+
type: p.type,
|
|
137
|
+
body: p.body,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// Tiny indirection so the static analyzer doesn't get confused if writeMemory
|
|
141
|
+
// later changes shape — runtime behavior unchanged.
|
|
142
|
+
function require_writeMemory_path(fn) {
|
|
143
|
+
return fn;
|
|
144
|
+
}
|
|
145
|
+
function extractJson(text) {
|
|
146
|
+
// Strip optional ```json fences and return the first {...} block.
|
|
147
|
+
const fenceMatch = text.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
148
|
+
const body = fenceMatch ? fenceMatch[1] : text;
|
|
149
|
+
const start = body.indexOf("{");
|
|
150
|
+
const end = body.lastIndexOf("}");
|
|
151
|
+
if (start === -1 || end === -1 || end < start)
|
|
152
|
+
return body;
|
|
153
|
+
return body.slice(start, end + 1);
|
|
154
|
+
}
|
|
155
|
+
// Re-export fs for tests that need to inspect proposal-application output.
|
|
156
|
+
export const _internal = { fs };
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/self-review/index.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
function sessionsDir(config) {
|
|
4
|
+
const dir = path.join(config.projectDir, "sessions");
|
|
5
|
+
if (!fs.existsSync(dir))
|
|
6
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
7
|
+
return dir;
|
|
8
|
+
}
|
|
9
|
+
function newSessionId() {
|
|
10
|
+
// ISO-ish timestamp safe for filenames + 4 random hex chars to disambiguate.
|
|
11
|
+
const now = new Date().toISOString().replace(/[:.]/g, "-");
|
|
12
|
+
const rnd = Math.floor(Math.random() * 0xffff).toString(16).padStart(4, "0");
|
|
13
|
+
return `${now}-${rnd}`;
|
|
14
|
+
}
|
|
15
|
+
function deriveFirstUserPreview(messages) {
|
|
16
|
+
for (const m of messages) {
|
|
17
|
+
if (m.role !== "user")
|
|
18
|
+
continue;
|
|
19
|
+
if (typeof m.content === "string")
|
|
20
|
+
return m.content.slice(0, 80);
|
|
21
|
+
if (Array.isArray(m.content)) {
|
|
22
|
+
const text = m.content.find((p) => p.type === "text");
|
|
23
|
+
if (text)
|
|
24
|
+
return text.text.slice(0, 80);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return "(no user message)";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Save the conversation as `${sessionId}.json` in the project's sessions dir.
|
|
31
|
+
* If no sessionId is given, a new one is minted and returned. Pass the same id
|
|
32
|
+
* back on subsequent saves within one run to keep updating the same file.
|
|
33
|
+
*/
|
|
34
|
+
export function saveSession(config, messages, sessionId) {
|
|
35
|
+
const dir = sessionsDir(config);
|
|
36
|
+
const id = sessionId ?? newSessionId();
|
|
37
|
+
const file = path.join(dir, `${id}.json`);
|
|
38
|
+
const data = {
|
|
39
|
+
id,
|
|
40
|
+
workdir: config.workdir,
|
|
41
|
+
model: config.model,
|
|
42
|
+
savedAt: new Date().toISOString(),
|
|
43
|
+
preview: deriveFirstUserPreview(messages),
|
|
44
|
+
messages,
|
|
45
|
+
};
|
|
46
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
47
|
+
return { id, file };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Load a session. With no id, returns the most recently saved one (legacy
|
|
51
|
+
* single-resume behavior, used by `--continue`).
|
|
52
|
+
*/
|
|
53
|
+
export function loadSession(config, sessionId) {
|
|
54
|
+
const dir = sessionsDir(config);
|
|
55
|
+
if (sessionId) {
|
|
56
|
+
const file = path.join(dir, `${sessionId}.json`);
|
|
57
|
+
if (!fs.existsSync(file)) {
|
|
58
|
+
// Also accept legacy "last.json" by exact name.
|
|
59
|
+
const legacy = path.join(dir, sessionId);
|
|
60
|
+
if (fs.existsSync(legacy))
|
|
61
|
+
return readSessionFile(legacy);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return readSessionFile(file);
|
|
65
|
+
}
|
|
66
|
+
const summaries = listSessions(config);
|
|
67
|
+
if (summaries.length === 0) {
|
|
68
|
+
// Legacy fallback: a single last.json from before A.3.
|
|
69
|
+
const legacy = path.join(dir, "last.json");
|
|
70
|
+
if (fs.existsSync(legacy))
|
|
71
|
+
return readSessionFile(legacy);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return readSessionFile(path.join(dir, `${summaries[0].id}.json`));
|
|
75
|
+
}
|
|
76
|
+
export function listSessions(config) {
|
|
77
|
+
const dir = sessionsDir(config);
|
|
78
|
+
const files = fs
|
|
79
|
+
.readdirSync(dir)
|
|
80
|
+
.filter((f) => f.endsWith(".json") && f !== "last.json")
|
|
81
|
+
.map((f) => path.join(dir, f));
|
|
82
|
+
const summaries = [];
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
const data = readSessionFile(file);
|
|
85
|
+
if (!data)
|
|
86
|
+
continue;
|
|
87
|
+
summaries.push({
|
|
88
|
+
id: data.id,
|
|
89
|
+
savedAt: data.savedAt,
|
|
90
|
+
preview: data.preview ?? "",
|
|
91
|
+
messageCount: data.messages.length,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
summaries.sort((a, b) => (a.savedAt < b.savedAt ? 1 : -1));
|
|
95
|
+
return summaries;
|
|
96
|
+
}
|
|
97
|
+
/** Snapshot the conversation as a *new* session id (used by /fork). */
|
|
98
|
+
export function forkSession(config, messages) {
|
|
99
|
+
return saveSession(config, messages);
|
|
100
|
+
}
|
|
101
|
+
function readSessionFile(file) {
|
|
102
|
+
try {
|
|
103
|
+
const raw = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
104
|
+
// Backfill `id` for legacy files saved before A.3.
|
|
105
|
+
if (!raw.id)
|
|
106
|
+
raw.id = path.basename(file, ".json");
|
|
107
|
+
return raw;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import matter from "gray-matter";
|
|
4
|
+
/**
|
|
5
|
+
* Skills live in <homeDir>/skills/<name>/SKILL.md or <workdir>/.claw/skills/<name>/SKILL.md.
|
|
6
|
+
* The frontmatter `name` and `description` describe the skill; the body is injected as
|
|
7
|
+
* additional system context when the user invokes it via /<name>.
|
|
8
|
+
*/
|
|
9
|
+
export function listSkills(config) {
|
|
10
|
+
const dirs = [
|
|
11
|
+
path.join(config.homeDir, "skills"),
|
|
12
|
+
path.join(config.workdir, ".claw", "skills"),
|
|
13
|
+
];
|
|
14
|
+
const skills = [];
|
|
15
|
+
for (const dir of dirs) {
|
|
16
|
+
if (!fs.existsSync(dir))
|
|
17
|
+
continue;
|
|
18
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
19
|
+
const skillFile = path.join(dir, entry, "SKILL.md");
|
|
20
|
+
if (!fs.existsSync(skillFile))
|
|
21
|
+
continue;
|
|
22
|
+
try {
|
|
23
|
+
const parsed = matter(fs.readFileSync(skillFile, "utf8"));
|
|
24
|
+
skills.push({
|
|
25
|
+
name: parsed.data?.name ?? entry,
|
|
26
|
+
description: parsed.data?.description ?? "",
|
|
27
|
+
body: parsed.content.trim(),
|
|
28
|
+
file: skillFile,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return skills;
|
|
35
|
+
}
|
|
36
|
+
export function findSkill(config, name) {
|
|
37
|
+
return listSkills(config).find((s) => s.name === name);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/skills/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAUjC;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,MAAkB;IAC3C,MAAM,IAAI,GAAG;QACX,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC;KAC7C,CAAC;IACF,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClC,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YACxC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;YACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;gBAAE,SAAS;YACxC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC1D,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,KAAK;oBAChC,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,WAAW,IAAI,EAAE;oBAC3C,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;oBAC3B,IAAI,EAAE,SAAS;iBAChB,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACZ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAkB,EAAE,IAAY;IACxD,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACzD,CAAC"}
|
package/dist/subagent.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { Agent } from "./agent.js";
|
|
5
|
+
import { getSubagentTools, getAllTools } from "./tools/index.js";
|
|
6
|
+
import { buildSystemPrompt } from "./prompts/system.js";
|
|
7
|
+
import { findSubagent } from "./subagents/index.js";
|
|
8
|
+
/**
|
|
9
|
+
* Spawn a subagent. Subagents have their own conversation but share the parent's
|
|
10
|
+
* config + permission manager. Their result is a single text string returned to
|
|
11
|
+
* the parent's Task tool call.
|
|
12
|
+
*/
|
|
13
|
+
export async function runSubagent(config, permissionCheck, req, onStatus) {
|
|
14
|
+
const kind = req.subagent_type ?? "general-purpose";
|
|
15
|
+
const def = findSubagent(config, kind);
|
|
16
|
+
// Pick the tool set. Order of resolution:
|
|
17
|
+
// 1. Registry def with explicit `tools:` frontmatter wins.
|
|
18
|
+
// 2. Builtin "explore" gets the read-only set via getSubagentTools.
|
|
19
|
+
// 3. Default to general-purpose tools (everything except Task).
|
|
20
|
+
let tools;
|
|
21
|
+
if (def?.tools) {
|
|
22
|
+
const all = getAllTools();
|
|
23
|
+
tools = all.filter((t) => def.tools.includes(t.name));
|
|
24
|
+
}
|
|
25
|
+
else if (kind === "explore") {
|
|
26
|
+
tools = getSubagentTools("explore");
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
tools = getSubagentTools("general-purpose");
|
|
30
|
+
}
|
|
31
|
+
// System prompt: a registered subagent's frontmatter body is appended to the
|
|
32
|
+
// standard subagent prompt. For the builtin types use the existing variants.
|
|
33
|
+
let variant = "subagent-general";
|
|
34
|
+
if (kind === "explore")
|
|
35
|
+
variant = "subagent-explore";
|
|
36
|
+
const baseExtras = [];
|
|
37
|
+
if (def && def.body) {
|
|
38
|
+
baseExtras.push(`# Subagent role\n${def.body}`);
|
|
39
|
+
}
|
|
40
|
+
const systemPrompt = buildSystemPrompt({ config, tools, variant, extras: baseExtras });
|
|
41
|
+
// Worktree isolation: spin up a temp git worktree and run the subagent there.
|
|
42
|
+
let runConfig = config;
|
|
43
|
+
let worktreePath = null;
|
|
44
|
+
let branchName = null;
|
|
45
|
+
if (req.isolation === "worktree") {
|
|
46
|
+
const setup = createWorktree(config, req.description);
|
|
47
|
+
if (!setup.ok)
|
|
48
|
+
return `Worktree setup failed: ${setup.error}`;
|
|
49
|
+
runConfig = { ...config, workdir: setup.path };
|
|
50
|
+
worktreePath = setup.path;
|
|
51
|
+
branchName = setup.branch;
|
|
52
|
+
onStatus?.(`[worktree] ${worktreePath}`);
|
|
53
|
+
}
|
|
54
|
+
const agent = new Agent({
|
|
55
|
+
config: runConfig,
|
|
56
|
+
tools,
|
|
57
|
+
permissionCheck,
|
|
58
|
+
spawnSubagent: undefined,
|
|
59
|
+
systemPromptExtras: [],
|
|
60
|
+
});
|
|
61
|
+
agent.conversation[0] = { role: "system", content: systemPrompt };
|
|
62
|
+
if (def?.modelRole) {
|
|
63
|
+
agent.setNextRole(def.modelRole);
|
|
64
|
+
}
|
|
65
|
+
agent.pushUser(req.prompt);
|
|
66
|
+
let result = "";
|
|
67
|
+
onStatus?.(`[subagent:${kind}] ${req.description}`);
|
|
68
|
+
await agent.run((evt) => {
|
|
69
|
+
if (evt.type === "text")
|
|
70
|
+
result = evt.data;
|
|
71
|
+
if (evt.type === "error") {
|
|
72
|
+
result = `Subagent error: ${evt.data}`;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
if (worktreePath) {
|
|
76
|
+
const diff = collectWorktreeDiff(worktreePath);
|
|
77
|
+
if (diff.trim() === "") {
|
|
78
|
+
// No changes — clean up immediately.
|
|
79
|
+
removeWorktree(config.workdir, worktreePath, branchName);
|
|
80
|
+
result = `${result}\n\n[worktree had no diff — auto-cleaned]`;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
result = `${result}\n\n[worktree retained: ${worktreePath}]\n[branch: ${branchName}]\n\n--- BEGIN DIFF ---\n${diff.slice(0, 50_000)}\n--- END DIFF ---`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result || "(subagent returned no output)";
|
|
87
|
+
}
|
|
88
|
+
function createWorktree(config, description) {
|
|
89
|
+
const slug = description.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 30).replace(/^-|-$/g, "");
|
|
90
|
+
const stamp = Date.now().toString(36);
|
|
91
|
+
const branch = `claw/${slug || "task"}-${stamp}`;
|
|
92
|
+
const wtDir = path.join(config.projectDir, "worktrees", `${slug || "task"}-${stamp}`);
|
|
93
|
+
fs.mkdirSync(path.dirname(wtDir), { recursive: true });
|
|
94
|
+
const res = spawnSync("git", ["worktree", "add", "-b", branch, wtDir], {
|
|
95
|
+
cwd: config.workdir,
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
});
|
|
98
|
+
if (res.status !== 0) {
|
|
99
|
+
return { ok: false, error: (res.stderr || res.stdout || "git worktree add failed").trim() };
|
|
100
|
+
}
|
|
101
|
+
return { ok: true, path: wtDir, branch };
|
|
102
|
+
}
|
|
103
|
+
function collectWorktreeDiff(worktreePath) {
|
|
104
|
+
const status = spawnSync("git", ["status", "--porcelain"], {
|
|
105
|
+
cwd: worktreePath,
|
|
106
|
+
encoding: "utf8",
|
|
107
|
+
});
|
|
108
|
+
if (status.status !== 0 || !status.stdout.trim()) {
|
|
109
|
+
// Maybe the agent committed; fall back to diff against HEAD~1.
|
|
110
|
+
const log = spawnSync("git", ["log", "--oneline", "@{u}..HEAD"], { cwd: worktreePath, encoding: "utf8" });
|
|
111
|
+
if (log.status === 0 && log.stdout.trim()) {
|
|
112
|
+
const diff = spawnSync("git", ["diff", "@{u}", "HEAD"], { cwd: worktreePath, encoding: "utf8" });
|
|
113
|
+
return diff.stdout ?? "";
|
|
114
|
+
}
|
|
115
|
+
return status.stdout?.trim() ? status.stdout : "";
|
|
116
|
+
}
|
|
117
|
+
// Stage and diff everything so untracked files show up too.
|
|
118
|
+
spawnSync("git", ["add", "-N", "."], { cwd: worktreePath });
|
|
119
|
+
const diff = spawnSync("git", ["diff"], { cwd: worktreePath, encoding: "utf8" });
|
|
120
|
+
return diff.stdout ?? "";
|
|
121
|
+
}
|
|
122
|
+
function removeWorktree(mainWorkdir, worktreePath, branch) {
|
|
123
|
+
spawnSync("git", ["worktree", "remove", "--force", worktreePath], { cwd: mainWorkdir });
|
|
124
|
+
if (branch) {
|
|
125
|
+
spawnSync("git", ["branch", "-D", branch], { cwd: mainWorkdir });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=subagent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent.js","sourceRoot":"","sources":["../src/subagent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAkB,EAClB,eAA+C,EAC/C,GAAoB,EACpB,QAAkC;IAElC,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,IAAI,iBAAiB,CAAC;IACpD,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEvC,0CAA0C;IAC1C,6DAA6D;IAC7D,sEAAsE;IACtE,kEAAkE;IAClE,IAAI,KAAK,CAAC;IACV,IAAI,GAAG,EAAE,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,CAAC;SAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,KAAK,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IAC9C,CAAC;IAED,6EAA6E;IAC7E,6EAA6E;IAC7E,IAAI,OAAO,GAAqD,kBAAkB,CAAC;IACnF,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,GAAG,kBAAkB,CAAC;IACrD,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACpB,UAAU,CAAC,IAAI,CAAC,oBAAoB,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,YAAY,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;IAEvF,8EAA8E;IAC9E,IAAI,SAAS,GAAG,MAAM,CAAC;IACvB,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,GAAG,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,CAAC,EAAE;YAAE,OAAO,0BAA0B,KAAK,CAAC,KAAK,EAAE,CAAC;QAC9D,SAAS,GAAG,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;QAC/C,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC;QAC1B,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1B,QAAQ,EAAE,CAAC,cAAc,YAAY,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,MAAM,EAAE,SAAS;QACjB,KAAK;QACL,eAAe;QACf,aAAa,EAAE,SAAS;QACxB,kBAAkB,EAAE,EAAE;KACvB,CAAC,CAAC;IACH,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IAClE,IAAI,GAAG,EAAE,SAAS,EAAE,CAAC;QACnB,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAE3B,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,QAAQ,EAAE,CAAC,aAAa,IAAI,KAAK,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IACpD,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM;YAAE,MAAM,GAAG,GAAG,CAAC,IAAc,CAAC;QACrD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACzB,MAAM,GAAG,mBAAmB,GAAG,CAAC,IAAI,EAAE,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACvB,qCAAqC;YACrC,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,MAAM,2CAA2C,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,GAAG,MAAM,2BAA2B,YAAY,eAAe,UAAU,4BAA4B,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,oBAAoB,CAAC;QAC1J,CAAC;IACH,CAAC;IACD,OAAO,MAAM,IAAI,+BAA+B,CAAC;AACnD,CAAC;AAED,SAAS,cAAc,CACrB,MAAkB,EAClB,WAAmB;IAEnB,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACtG,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,QAAQ,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC,CAAC;IACtF,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QACrE,GAAG,EAAE,MAAM,CAAC,OAAO;QACnB,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IACH,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,yBAAyB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;IAC9F,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC3C,CAAC;AAED,SAAS,mBAAmB,CAAC,YAAoB;IAC/C,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE;QACzD,GAAG,EAAE,YAAY;QACjB,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACjD,+DAA+D;QAC/D,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1G,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;YACjG,OAAO,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,CAAC;IACD,4DAA4D;IAC5D,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACjF,OAAO,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,SAAS,cAAc,CAAC,WAAmB,EAAE,YAAoB,EAAE,MAAqB;IACtF,SAAS,CAAC,KAAK,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IACxF,IAAI,MAAM,EAAE,CAAC;QACX,SAAS,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IACnE,CAAC;AACH,CAAC"}
|