vibecheck-agent 1.0.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.
Files changed (46) hide show
  1. package/dist/agent.d.ts +32 -0
  2. package/dist/agent.js +431 -0
  3. package/dist/agent.js.map +1 -0
  4. package/dist/claude.d.ts +52 -0
  5. package/dist/claude.js +246 -0
  6. package/dist/claude.js.map +1 -0
  7. package/dist/config.d.ts +12 -0
  8. package/dist/config.js +57 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/images.d.ts +19 -0
  11. package/dist/images.js +101 -0
  12. package/dist/images.js.map +1 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +54 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/protocol.d.ts +163 -0
  17. package/dist/protocol.js +3 -0
  18. package/dist/protocol.js.map +1 -0
  19. package/dist/scheduler.d.ts +33 -0
  20. package/dist/scheduler.js +132 -0
  21. package/dist/scheduler.js.map +1 -0
  22. package/dist/screenshot.d.ts +28 -0
  23. package/dist/screenshot.js +356 -0
  24. package/dist/screenshot.js.map +1 -0
  25. package/dist/security.d.ts +25 -0
  26. package/dist/security.js +117 -0
  27. package/dist/security.js.map +1 -0
  28. package/dist/session.d.ts +4 -0
  29. package/dist/session.js +55 -0
  30. package/dist/session.js.map +1 -0
  31. package/dist/skills.d.ts +25 -0
  32. package/dist/skills.js +91 -0
  33. package/dist/skills.js.map +1 -0
  34. package/package.json +43 -0
  35. package/src/agent.ts +557 -0
  36. package/src/claude.ts +322 -0
  37. package/src/config.ts +61 -0
  38. package/src/images.ts +122 -0
  39. package/src/index.ts +73 -0
  40. package/src/protocol.ts +229 -0
  41. package/src/scheduler.ts +173 -0
  42. package/src/screenshot.ts +387 -0
  43. package/src/security.ts +158 -0
  44. package/src/session.ts +68 -0
  45. package/src/skills.ts +122 -0
  46. package/tsconfig.json +18 -0
@@ -0,0 +1,356 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { execSync, spawn } from "node:child_process";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import net from "node:net";
6
+ /**
7
+ * Detect project type from directory contents.
8
+ */
9
+ export function detectProjectType(projectDir) {
10
+ // Check package.json for Node.js projects
11
+ const pkgPath = path.join(projectDir, "package.json");
12
+ if (existsSync(pkgPath)) {
13
+ try {
14
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
15
+ const deps = {
16
+ ...pkg.dependencies,
17
+ ...pkg.devDependencies,
18
+ };
19
+ if (deps["next"]) {
20
+ return {
21
+ type: "node",
22
+ framework: "Next.js",
23
+ cmd: "npx next dev",
24
+ port: 3000,
25
+ };
26
+ }
27
+ if (deps["vite"]) {
28
+ return {
29
+ type: "node",
30
+ framework: "Vite",
31
+ cmd: "npx vite",
32
+ port: 5173,
33
+ };
34
+ }
35
+ if (deps["react-scripts"]) {
36
+ return {
37
+ type: "node",
38
+ framework: "Create React App",
39
+ cmd: "npx react-scripts start",
40
+ port: 3000,
41
+ };
42
+ }
43
+ if (deps["@vue/cli-service"]) {
44
+ return {
45
+ type: "node",
46
+ framework: "Vue CLI",
47
+ cmd: "npx vue-cli-service serve",
48
+ port: 8080,
49
+ };
50
+ }
51
+ // Generic Node.js with start script
52
+ if (pkg.scripts?.dev) {
53
+ return {
54
+ type: "node",
55
+ framework: "Node.js",
56
+ cmd: "npm run dev",
57
+ port: 3000,
58
+ };
59
+ }
60
+ if (pkg.scripts?.start) {
61
+ return {
62
+ type: "node",
63
+ framework: "Node.js",
64
+ cmd: "npm start",
65
+ port: 3000,
66
+ };
67
+ }
68
+ }
69
+ catch {
70
+ // ignore parse errors
71
+ }
72
+ }
73
+ // Check for Python projects
74
+ const requirementsPath = path.join(projectDir, "requirements.txt");
75
+ const pyprojectPath = path.join(projectDir, "pyproject.toml");
76
+ if (existsSync(requirementsPath) || existsSync(pyprojectPath)) {
77
+ // Check for Streamlit
78
+ for (const name of [
79
+ "app.py",
80
+ "main.py",
81
+ "streamlit_app.py",
82
+ ]) {
83
+ const fp = path.join(projectDir, name);
84
+ if (existsSync(fp)) {
85
+ try {
86
+ const content = readFileSync(fp, "utf-8");
87
+ if (content.includes("streamlit")) {
88
+ return {
89
+ type: "python",
90
+ framework: "Streamlit",
91
+ cmd: `streamlit run ${name}`,
92
+ entry: name,
93
+ port: 8501,
94
+ };
95
+ }
96
+ }
97
+ catch {
98
+ // ignore
99
+ }
100
+ }
101
+ }
102
+ // Check for FastAPI
103
+ const mainPy = path.join(projectDir, "main.py");
104
+ if (existsSync(mainPy)) {
105
+ try {
106
+ const content = readFileSync(mainPy, "utf-8");
107
+ if (content.includes("fastapi") || content.includes("FastAPI")) {
108
+ return {
109
+ type: "python",
110
+ framework: "FastAPI",
111
+ cmd: "uvicorn main:app --reload",
112
+ entry: "main.py",
113
+ port: 8000,
114
+ };
115
+ }
116
+ }
117
+ catch {
118
+ // ignore
119
+ }
120
+ }
121
+ // Check for Flask
122
+ const appPy = path.join(projectDir, "app.py");
123
+ if (existsSync(appPy)) {
124
+ try {
125
+ const content = readFileSync(appPy, "utf-8");
126
+ if (content.includes("flask") || content.includes("Flask")) {
127
+ return {
128
+ type: "python",
129
+ framework: "Flask",
130
+ cmd: "flask run",
131
+ entry: "app.py",
132
+ port: 5000,
133
+ };
134
+ }
135
+ }
136
+ catch {
137
+ // ignore
138
+ }
139
+ }
140
+ }
141
+ // Check for static HTML
142
+ const indexHtml = path.join(projectDir, "index.html");
143
+ if (existsSync(indexHtml)) {
144
+ return {
145
+ type: "static",
146
+ framework: null,
147
+ entry: "index.html",
148
+ };
149
+ }
150
+ // Look for any HTML file
151
+ try {
152
+ const files = readdirSync(projectDir);
153
+ const htmlFile = files.find((f) => f.endsWith(".html"));
154
+ if (htmlFile) {
155
+ return {
156
+ type: "static",
157
+ framework: null,
158
+ entry: htmlFile,
159
+ };
160
+ }
161
+ }
162
+ catch {
163
+ // ignore
164
+ }
165
+ return { type: "unknown", framework: null };
166
+ }
167
+ /**
168
+ * Wait for a port to become available.
169
+ */
170
+ function waitForPort(port, timeoutMs) {
171
+ return new Promise((resolve) => {
172
+ const start = Date.now();
173
+ const check = () => {
174
+ const socket = new net.Socket();
175
+ socket.setTimeout(500);
176
+ socket.once("connect", () => {
177
+ socket.destroy();
178
+ resolve(true);
179
+ });
180
+ socket.once("error", () => {
181
+ socket.destroy();
182
+ if (Date.now() - start > timeoutMs) {
183
+ resolve(false);
184
+ }
185
+ else {
186
+ setTimeout(check, 500);
187
+ }
188
+ });
189
+ socket.once("timeout", () => {
190
+ socket.destroy();
191
+ if (Date.now() - start > timeoutMs) {
192
+ resolve(false);
193
+ }
194
+ else {
195
+ setTimeout(check, 500);
196
+ }
197
+ });
198
+ socket.connect(port, "127.0.0.1");
199
+ };
200
+ check();
201
+ });
202
+ }
203
+ /**
204
+ * Take a screenshot of an HTML file using Playwright.
205
+ */
206
+ export async function htmlFileToScreenshot(htmlPath, outputPath) {
207
+ try {
208
+ const { chromium } = await import("playwright");
209
+ const browser = await chromium.launch({ headless: true });
210
+ const page = await browser.newPage({
211
+ viewport: { width: 1200, height: 800 },
212
+ });
213
+ await page.goto(`file://${path.resolve(htmlPath)}`);
214
+ await page.waitForTimeout(500);
215
+ await page.screenshot({ path: outputPath, fullPage: true });
216
+ await browser.close();
217
+ return outputPath;
218
+ }
219
+ catch (e) {
220
+ console.error("[screenshot] HTML screenshot failed:", e);
221
+ return null;
222
+ }
223
+ }
224
+ /**
225
+ * Take a screenshot of a project by starting its dev server.
226
+ */
227
+ export async function screenshotProject(projectDir, outputPath) {
228
+ const info = detectProjectType(projectDir);
229
+ // Static HTML: screenshot directly
230
+ if (info.type === "static" && info.entry) {
231
+ const htmlPath = path.join(projectDir, info.entry);
232
+ return htmlFileToScreenshot(htmlPath, outputPath);
233
+ }
234
+ // Dev server projects: start server, screenshot, kill
235
+ if (!info.cmd || !info.port) {
236
+ return null;
237
+ }
238
+ let serverProcess = null;
239
+ try {
240
+ // Ensure npm dependencies are installed for Node.js projects
241
+ if (info.type === "node" &&
242
+ !existsSync(path.join(projectDir, "node_modules"))) {
243
+ console.log("[screenshot] Running npm install...");
244
+ execSync("npm install", {
245
+ cwd: projectDir,
246
+ timeout: 60_000,
247
+ stdio: "ignore",
248
+ });
249
+ }
250
+ // Start dev server
251
+ const [cmd, ...args] = info.cmd.split(" ");
252
+ serverProcess = spawn(cmd, args, {
253
+ cwd: projectDir,
254
+ stdio: "ignore",
255
+ detached: true,
256
+ env: { ...process.env, PORT: String(info.port), BROWSER: "none" },
257
+ });
258
+ // Wait for port to open
259
+ const portReady = await waitForPort(info.port, 30_000);
260
+ if (!portReady) {
261
+ console.warn("[screenshot] Server start timeout (30s)");
262
+ return null;
263
+ }
264
+ // Take screenshot
265
+ try {
266
+ const { chromium } = await import("playwright");
267
+ const browser = await chromium.launch({ headless: true });
268
+ const page = await browser.newPage({
269
+ viewport: { width: 1200, height: 800 },
270
+ });
271
+ await page.goto(`http://localhost:${info.port}`, {
272
+ waitUntil: "networkidle",
273
+ timeout: 15_000,
274
+ });
275
+ await page.screenshot({ path: outputPath, fullPage: true });
276
+ await browser.close();
277
+ return outputPath;
278
+ }
279
+ catch (e) {
280
+ console.error("[screenshot] Screenshot capture failed:", e);
281
+ return null;
282
+ }
283
+ }
284
+ catch (e) {
285
+ console.error("[screenshot] Project screenshot failed:", e);
286
+ return null;
287
+ }
288
+ finally {
289
+ // Kill dev server process group
290
+ if (serverProcess && serverProcess.pid) {
291
+ try {
292
+ process.kill(-serverProcess.pid, "SIGTERM");
293
+ }
294
+ catch {
295
+ try {
296
+ serverProcess.kill("SIGKILL");
297
+ }
298
+ catch {
299
+ // ignore
300
+ }
301
+ }
302
+ }
303
+ }
304
+ }
305
+ /**
306
+ * Find the project directory from a message or response text.
307
+ * Looks for paths that contain typical project indicators.
308
+ */
309
+ export function findProjectDir(text, workDir) {
310
+ // Check if workDir itself is a project
311
+ const info = detectProjectType(workDir);
312
+ if (info.type !== "unknown") {
313
+ return workDir;
314
+ }
315
+ // Look for directory paths in the text
316
+ const dirRe = /(\/[^\s:*?"<>|]+)/g;
317
+ let match;
318
+ while ((match = dirRe.exec(text)) !== null) {
319
+ const dir = match[1];
320
+ try {
321
+ const projInfo = detectProjectType(dir);
322
+ if (projInfo.type !== "unknown") {
323
+ return dir;
324
+ }
325
+ }
326
+ catch {
327
+ // not a valid directory
328
+ }
329
+ }
330
+ // Check subdirectories of workDir (1 level deep)
331
+ try {
332
+ const entries = readdirSync(workDir, { withFileTypes: true });
333
+ for (const entry of entries) {
334
+ if (entry.isDirectory() &&
335
+ !entry.name.startsWith(".") &&
336
+ entry.name !== "node_modules") {
337
+ const subDir = path.join(workDir, entry.name);
338
+ const subInfo = detectProjectType(subDir);
339
+ if (subInfo.type !== "unknown") {
340
+ return subDir;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ catch {
346
+ // ignore
347
+ }
348
+ return null;
349
+ }
350
+ /**
351
+ * Get screenshot output path.
352
+ */
353
+ export function getScreenshotPath() {
354
+ return path.join(os.tmpdir(), "vibecheck_screenshot.png");
355
+ }
356
+ //# sourceMappingURL=screenshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { PermissionResult } from "@anthropic-ai/claude-agent-sdk";
2
+ export declare class SecurityManager {
3
+ private trustedPaths;
4
+ private pendingApproval;
5
+ /** Callback to send approval_required to WebSocket */
6
+ onApprovalNeeded?: (paths: string[], toolName: string, input: Record<string, unknown>) => void;
7
+ constructor(workDir: string);
8
+ addTrustedPath(p: string): void;
9
+ isPathTrusted(p: string): boolean;
10
+ isSafeCommand(command: string): boolean;
11
+ /**
12
+ * SDK canUseTool callback.
13
+ * Matches the exact CanUseTool signature from @anthropic-ai/claude-agent-sdk.
14
+ */
15
+ canUseTool: (toolName: string, input: Record<string, unknown>, options: {
16
+ signal: AbortSignal;
17
+ }) => Promise<PermissionResult>;
18
+ /**
19
+ * Called by agent when approval/denial arrives from server.
20
+ */
21
+ resolveApproval(approved: boolean, permanent: boolean): void;
22
+ private extractPathsFromToolInput;
23
+ }
24
+ /** Extract absolute paths from text (for Bash commands, user messages, etc.) */
25
+ export declare function extractPathsFromText(text: string): string[];
@@ -0,0 +1,117 @@
1
+ import path from "node:path";
2
+ import { SAFE_SYSTEM_COMMANDS } from "./config.js";
3
+ // Regex patterns to extract paths from text
4
+ const ABSOLUTE_PATH_RE = /(?:^|\s)(\/[^\s:*?"<>|]+)/g;
5
+ const RELATIVE_PATH_RE = /(?:^|\s)(\.\.?\/[^\s:*?"<>|]+)/g;
6
+ export class SecurityManager {
7
+ trustedPaths;
8
+ pendingApproval = null;
9
+ /** Callback to send approval_required to WebSocket */
10
+ onApprovalNeeded;
11
+ constructor(workDir) {
12
+ this.trustedPaths = new Set([path.resolve(workDir)]);
13
+ }
14
+ addTrustedPath(p) {
15
+ const normalized = path.resolve(p);
16
+ this.trustedPaths.add(normalized);
17
+ console.log(`[security] Trusted path added: ${normalized}`);
18
+ }
19
+ isPathTrusted(p) {
20
+ const normalized = path.resolve(p);
21
+ for (const trusted of this.trustedPaths) {
22
+ if (normalized === trusted ||
23
+ normalized.startsWith(trusted + path.sep)) {
24
+ return true;
25
+ }
26
+ }
27
+ return false;
28
+ }
29
+ isSafeCommand(command) {
30
+ const trimmed = command.trim();
31
+ for (const safe of SAFE_SYSTEM_COMMANDS) {
32
+ if (trimmed === safe || trimmed.startsWith(safe + " ")) {
33
+ return true;
34
+ }
35
+ }
36
+ return false;
37
+ }
38
+ /**
39
+ * SDK canUseTool callback.
40
+ * Matches the exact CanUseTool signature from @anthropic-ai/claude-agent-sdk.
41
+ */
42
+ canUseTool = async (toolName, input, options) => {
43
+ const paths = this.extractPathsFromToolInput(toolName, input);
44
+ const untrusted = paths.filter((p) => !this.isPathTrusted(p));
45
+ // Trusted paths or safe commands → allow immediately
46
+ if (untrusted.length === 0) {
47
+ return { behavior: "allow", updatedInput: input };
48
+ }
49
+ if (toolName === "Bash" &&
50
+ typeof input.command === "string" &&
51
+ this.isSafeCommand(input.command)) {
52
+ return { behavior: "allow", updatedInput: input };
53
+ }
54
+ // Request approval from web UI via WebSocket
55
+ this.onApprovalNeeded?.(untrusted, toolName, input);
56
+ // Wait for user response (Promise resolved by resolveApproval)
57
+ return new Promise((resolve) => {
58
+ this.pendingApproval = { resolve, toolName, input };
59
+ // Handle abort signal
60
+ options.signal.addEventListener("abort", () => {
61
+ this.pendingApproval = null;
62
+ resolve({ behavior: "deny", message: "Operation aborted" });
63
+ }, { once: true });
64
+ });
65
+ };
66
+ /**
67
+ * Called by agent when approval/denial arrives from server.
68
+ */
69
+ resolveApproval(approved, permanent) {
70
+ if (!this.pendingApproval)
71
+ return;
72
+ const { resolve, toolName, input } = this.pendingApproval;
73
+ if (approved) {
74
+ if (permanent) {
75
+ const paths = this.extractPathsFromToolInput(toolName, input);
76
+ paths.forEach((p) => this.addTrustedPath(p));
77
+ }
78
+ resolve({ behavior: "allow", updatedInput: input });
79
+ }
80
+ else {
81
+ resolve({ behavior: "deny", message: "User denied the request" });
82
+ }
83
+ this.pendingApproval = null;
84
+ }
85
+ extractPathsFromToolInput(toolName, input) {
86
+ switch (toolName) {
87
+ case "Read":
88
+ case "Write":
89
+ case "Edit":
90
+ return typeof input.file_path === "string" ? [input.file_path] : [];
91
+ case "Bash":
92
+ return typeof input.command === "string"
93
+ ? extractPathsFromText(input.command)
94
+ : [];
95
+ case "Glob":
96
+ case "Grep":
97
+ return typeof input.path === "string" ? [input.path] : [];
98
+ default:
99
+ return [];
100
+ }
101
+ }
102
+ }
103
+ /** Extract absolute paths from text (for Bash commands, user messages, etc.) */
104
+ export function extractPathsFromText(text) {
105
+ const paths = [];
106
+ let match;
107
+ ABSOLUTE_PATH_RE.lastIndex = 0;
108
+ while ((match = ABSOLUTE_PATH_RE.exec(text)) !== null) {
109
+ paths.push(match[1]);
110
+ }
111
+ RELATIVE_PATH_RE.lastIndex = 0;
112
+ while ((match = RELATIVE_PATH_RE.exec(text)) !== null) {
113
+ paths.push(match[1]);
114
+ }
115
+ return paths;
116
+ }
117
+ //# sourceMappingURL=security.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare function getSessionFilePath(workDir: string): string;
2
+ export declare function saveSessionId(workDir: string, sessionId: string): void;
3
+ export declare function loadSessionId(workDir: string): string | null;
4
+ export declare function clearSessionId(workDir: string): void;
@@ -0,0 +1,55 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, } from "node:fs";
3
+ import path from "node:path";
4
+ import { SESSION_DIR } from "./config.js";
5
+ export function getSessionFilePath(workDir) {
6
+ const hash = createHash("md5").update(workDir).digest("hex").slice(0, 12);
7
+ return path.join(SESSION_DIR, `session_${hash}.json`);
8
+ }
9
+ export function saveSessionId(workDir, sessionId) {
10
+ try {
11
+ if (!existsSync(SESSION_DIR)) {
12
+ mkdirSync(SESSION_DIR, { recursive: true });
13
+ }
14
+ const data = {
15
+ work_dir: workDir,
16
+ session_id: sessionId,
17
+ updated_at: new Date().toISOString(),
18
+ };
19
+ writeFileSync(getSessionFilePath(workDir), JSON.stringify(data, null, 2));
20
+ console.log(`[session] Session ID saved: ${sessionId.slice(0, 20)}...`);
21
+ }
22
+ catch (e) {
23
+ console.error(`[session] Failed to save session ID:`, e);
24
+ }
25
+ }
26
+ export function loadSessionId(workDir) {
27
+ try {
28
+ const filePath = getSessionFilePath(workDir);
29
+ if (!existsSync(filePath))
30
+ return null;
31
+ const raw = readFileSync(filePath, "utf-8");
32
+ const data = JSON.parse(raw);
33
+ if (data.session_id && path.resolve(data.work_dir) === path.resolve(workDir)) {
34
+ console.log(`[session] Session ID loaded: ${data.session_id.slice(0, 20)}...`);
35
+ return data.session_id;
36
+ }
37
+ return null;
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ export function clearSessionId(workDir) {
44
+ try {
45
+ const filePath = getSessionFilePath(workDir);
46
+ if (existsSync(filePath)) {
47
+ unlinkSync(filePath);
48
+ console.log("[session] Session ID cleared");
49
+ }
50
+ }
51
+ catch {
52
+ // ignore
53
+ }
54
+ }
55
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,UAAU,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ1C,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,IAAI,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,SAAiB;IAC9D,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7B,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,IAAI,GAAgB;YACxB,QAAQ,EAAE,OAAO;YACjB,UAAU,EAAE,SAAS;YACrB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC,CAAC;QACF,aAAa,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,+BAA+B,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAgB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7E,OAAO,CAAC,GAAG,CACT,gCAAgC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAClE,CAAC;YACF,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Skills System
3
+ *
4
+ * A skill is a named preset that combines:
5
+ * - systemPrompt override → specialises Claude's behaviour
6
+ * - allowedTools override → restricts/expands what Claude may do
7
+ *
8
+ * Agent types (read-only research vs. full coding) are expressed as skills.
9
+ */
10
+ export interface Skill {
11
+ id: string;
12
+ name: string;
13
+ icon: string;
14
+ description: string;
15
+ /** Prepended / appended to Claude's default system prompt */
16
+ systemPrompt?: string;
17
+ /**
18
+ * If set, only these tools are available for this skill.
19
+ * Leave undefined to inherit the global allowedTools list.
20
+ */
21
+ allowedTools?: string[];
22
+ }
23
+ export declare const DEFAULT_SKILLS: Skill[];
24
+ export declare function getSkill(id: string): Skill | undefined;
25
+ export declare function getAllSkills(): Skill[];