lovecode-ai 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/README.md +175 -0
- package/bin/lovecode.js +5 -0
- package/dist/browser-UA4QOMPS.js +37 -0
- package/dist/chunk-FMT77EJQ.js +160 -0
- package/dist/chunk-G7VQGYJW.js +858 -0
- package/dist/chunk-IVAMLKMS.js +194 -0
- package/dist/chunk-LKUWOZUZ.js +0 -0
- package/dist/chunk-MOZHR2QY.js +412 -0
- package/dist/git-TBOGPTY4.js +70 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +7783 -0
- package/dist/playwright-N7OAVW2N.js +36 -0
- package/dist/registry-MW5ISDO7.js +32 -0
- package/dist/theme-ZRZYRB2Q.js +18 -0
- package/package.json +75 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// src/browser/playwright.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
var _require = createRequire(import.meta.url);
|
|
6
|
+
var defaultConfig = {
|
|
7
|
+
headless: true,
|
|
8
|
+
viewport: { width: 1280, height: 800 },
|
|
9
|
+
timeout: 3e4,
|
|
10
|
+
screenshotDir: ".lovecode/screenshots"
|
|
11
|
+
};
|
|
12
|
+
var currentBrowser = null;
|
|
13
|
+
var currentPage = null;
|
|
14
|
+
var config = { ...defaultConfig };
|
|
15
|
+
function playwrightAvailable() {
|
|
16
|
+
try {
|
|
17
|
+
_require.resolve("playwright");
|
|
18
|
+
return true;
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function isPlaywrightAvailable() {
|
|
24
|
+
return playwrightAvailable();
|
|
25
|
+
}
|
|
26
|
+
async function launchBrowser(cfg) {
|
|
27
|
+
if (!isPlaywrightAvailable()) {
|
|
28
|
+
throw new Error("Playwright not installed. Run: npm install playwright && npx playwright install chromium");
|
|
29
|
+
}
|
|
30
|
+
config = { ...defaultConfig, ...cfg };
|
|
31
|
+
if (currentBrowser) await closeBrowser();
|
|
32
|
+
const { chromium } = await import("playwright");
|
|
33
|
+
const headless = config.headless;
|
|
34
|
+
currentBrowser = await chromium.launch({ headless });
|
|
35
|
+
currentPage = await currentBrowser.newPage();
|
|
36
|
+
await currentPage.setViewportSize(config.viewport);
|
|
37
|
+
}
|
|
38
|
+
async function closeBrowser() {
|
|
39
|
+
if (currentBrowser) {
|
|
40
|
+
await currentBrowser.close().catch(() => {
|
|
41
|
+
});
|
|
42
|
+
currentBrowser = null;
|
|
43
|
+
currentPage = null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function isBrowserRunning() {
|
|
47
|
+
return currentBrowser !== null && currentPage !== null;
|
|
48
|
+
}
|
|
49
|
+
async function goto(url, timeout) {
|
|
50
|
+
ensurePage();
|
|
51
|
+
await currentPage.goto(url, { waitUntil: "networkidle", timeout: timeout || config.timeout });
|
|
52
|
+
return `Navigated to ${url}`;
|
|
53
|
+
}
|
|
54
|
+
async function click(selector) {
|
|
55
|
+
ensurePage();
|
|
56
|
+
await currentPage.waitForSelector(selector, { timeout: config.timeout });
|
|
57
|
+
await currentPage.click(selector);
|
|
58
|
+
return `Clicked: ${selector}`;
|
|
59
|
+
}
|
|
60
|
+
async function type(selector, text) {
|
|
61
|
+
ensurePage();
|
|
62
|
+
await currentPage.waitForSelector(selector, { timeout: config.timeout });
|
|
63
|
+
await currentPage.fill(selector, text);
|
|
64
|
+
return `Typed "${text}" into ${selector}`;
|
|
65
|
+
}
|
|
66
|
+
async function select(selector, value) {
|
|
67
|
+
ensurePage();
|
|
68
|
+
await currentPage.waitForSelector(selector, { timeout: config.timeout });
|
|
69
|
+
await currentPage.selectOption(selector, value);
|
|
70
|
+
return `Selected "${value}" in ${selector}`;
|
|
71
|
+
}
|
|
72
|
+
async function screenshot(name) {
|
|
73
|
+
ensurePage();
|
|
74
|
+
const screenshotDir = config.screenshotDir;
|
|
75
|
+
if (!fs.existsSync(screenshotDir)) {
|
|
76
|
+
fs.mkdirSync(screenshotDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
const timestamp = Date.now();
|
|
79
|
+
const fileName = name ? `${name}-${timestamp}.png` : `screenshot-${timestamp}.png`;
|
|
80
|
+
const filePath = path.join(screenshotDir, fileName);
|
|
81
|
+
await currentPage.screenshot({ path: filePath, fullPage: true });
|
|
82
|
+
return { path: filePath, width: config.viewport.width, height: config.viewport.height, timestamp };
|
|
83
|
+
}
|
|
84
|
+
async function inspect(selector) {
|
|
85
|
+
ensurePage();
|
|
86
|
+
await currentPage.waitForSelector(selector, { timeout: config.timeout });
|
|
87
|
+
const script = `
|
|
88
|
+
(sel) => {
|
|
89
|
+
const el = document.querySelector(sel);
|
|
90
|
+
if (!el) return null;
|
|
91
|
+
function describe(node) {
|
|
92
|
+
return {
|
|
93
|
+
tag: node.tagName.toLowerCase(),
|
|
94
|
+
text: (node.textContent || '').slice(0, 200),
|
|
95
|
+
attributes: Object.fromEntries(Array.from(node.attributes).map(a => [a.name, a.value])),
|
|
96
|
+
visible: node.offsetParent !== null,
|
|
97
|
+
children: Array.from(node.children).map(c => describe(c)),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return describe(el);
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
103
|
+
const result = await currentPage.evaluate(new Function("sel", `return (${script})(sel);`), selector);
|
|
104
|
+
return result || null;
|
|
105
|
+
}
|
|
106
|
+
async function getHTML(selector) {
|
|
107
|
+
ensurePage();
|
|
108
|
+
if (selector) {
|
|
109
|
+
const script = `document.querySelector("${selector}") ? document.querySelector("${selector}").outerHTML : 'Element not found'`;
|
|
110
|
+
return await currentPage.evaluate(script);
|
|
111
|
+
}
|
|
112
|
+
return await currentPage.evaluate("document.documentElement.outerHTML");
|
|
113
|
+
}
|
|
114
|
+
async function evaluate(script) {
|
|
115
|
+
ensurePage();
|
|
116
|
+
return await currentPage.evaluate(script);
|
|
117
|
+
}
|
|
118
|
+
async function waitFor(ms) {
|
|
119
|
+
ensurePage();
|
|
120
|
+
await currentPage.waitForTimeout(ms);
|
|
121
|
+
}
|
|
122
|
+
async function runActions(actions) {
|
|
123
|
+
const results = [];
|
|
124
|
+
for (const action of actions) {
|
|
125
|
+
switch (action.type) {
|
|
126
|
+
case "goto":
|
|
127
|
+
results.push(await goto(action.url));
|
|
128
|
+
break;
|
|
129
|
+
case "click":
|
|
130
|
+
results.push(await click(action.selector));
|
|
131
|
+
break;
|
|
132
|
+
case "type":
|
|
133
|
+
results.push(await type(action.selector, action.value));
|
|
134
|
+
break;
|
|
135
|
+
case "select":
|
|
136
|
+
results.push(await select(action.selector, action.value));
|
|
137
|
+
break;
|
|
138
|
+
case "screenshot":
|
|
139
|
+
results.push(`Screenshot saved: ${(await screenshot(action.value)).path}`);
|
|
140
|
+
break;
|
|
141
|
+
case "inspect":
|
|
142
|
+
results.push(JSON.stringify(await inspect(action.selector), null, 2));
|
|
143
|
+
break;
|
|
144
|
+
case "wait":
|
|
145
|
+
await waitFor(action.waitFor || 1e3);
|
|
146
|
+
results.push(`Waited ${action.waitFor || 1e3}ms`);
|
|
147
|
+
break;
|
|
148
|
+
case "evaluate":
|
|
149
|
+
results.push(String(await evaluate(action.script)));
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
155
|
+
function formatScreenshotResult(result) {
|
|
156
|
+
return `Screenshot: ${result.path} (${result.width}x${result.height})`;
|
|
157
|
+
}
|
|
158
|
+
function formatDOMElement(el, indent = 0) {
|
|
159
|
+
const pad = " ".repeat(indent);
|
|
160
|
+
const attrs = Object.entries(el.attributes).map(([k, v]) => `${k}="${v}"`).join(" ");
|
|
161
|
+
const visible = el.visible ? "" : " [hidden]";
|
|
162
|
+
const text = el.text ? ` "${el.text.slice(0, 60)}"` : "";
|
|
163
|
+
let result = `${pad}<${el.tag}${attrs ? " " + attrs : ""}>${text}${visible}`;
|
|
164
|
+
for (const child of el.children.slice(0, 10)) {
|
|
165
|
+
result += "\n" + formatDOMElement(child, indent + 1);
|
|
166
|
+
}
|
|
167
|
+
if (el.children.length > 10) {
|
|
168
|
+
result += `
|
|
169
|
+
${pad} ... and ${el.children.length - 10} more children`;
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
function ensurePage() {
|
|
174
|
+
if (!currentPage) throw new Error("Browser not launched. Call lovecode browser start first.");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export {
|
|
178
|
+
isPlaywrightAvailable,
|
|
179
|
+
launchBrowser,
|
|
180
|
+
closeBrowser,
|
|
181
|
+
isBrowserRunning,
|
|
182
|
+
goto,
|
|
183
|
+
click,
|
|
184
|
+
type,
|
|
185
|
+
select,
|
|
186
|
+
screenshot,
|
|
187
|
+
inspect,
|
|
188
|
+
getHTML,
|
|
189
|
+
evaluate,
|
|
190
|
+
waitFor,
|
|
191
|
+
runActions,
|
|
192
|
+
formatScreenshotResult,
|
|
193
|
+
formatDOMElement
|
|
194
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
// src/plugin/registry.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
|
|
6
|
+
// src/plugin/types.ts
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
var PLUGIN_DIR = ".lovecode/plugins";
|
|
9
|
+
var builtinPlugins = [
|
|
10
|
+
{
|
|
11
|
+
name: "docker",
|
|
12
|
+
description: "Docker container management and Dockerfile creation",
|
|
13
|
+
tags: ["docker", "container", "devops"],
|
|
14
|
+
create: () => {
|
|
15
|
+
const tools = [
|
|
16
|
+
{
|
|
17
|
+
name: "docker_ps",
|
|
18
|
+
description: "List running Docker containers",
|
|
19
|
+
usage: "[all=<true>]",
|
|
20
|
+
execute(_wd, args) {
|
|
21
|
+
const flag = args.all === "true" ? "-a" : "";
|
|
22
|
+
try {
|
|
23
|
+
const out = execSync(`docker ps ${flag}`, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
24
|
+
return { success: true, output: out || "No containers" };
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return { success: false, output: "", error: String(e) };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "docker_images",
|
|
32
|
+
description: "List Docker images",
|
|
33
|
+
usage: "",
|
|
34
|
+
execute() {
|
|
35
|
+
try {
|
|
36
|
+
const out = execSync("docker images", { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
37
|
+
return { success: true, output: out || "No images" };
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return { success: false, output: "", error: String(e) };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "docker_compose_up",
|
|
45
|
+
description: "Start Docker Compose services",
|
|
46
|
+
usage: "[file=<path>] [detach=<true>]",
|
|
47
|
+
execute(_wd, args) {
|
|
48
|
+
try {
|
|
49
|
+
const file = args.file ? `-f ${args.file}` : "";
|
|
50
|
+
const detach = args.detach === "true" ? "-d" : "";
|
|
51
|
+
const out = execSync(`docker compose ${file} up ${detach}`, { encoding: "utf-8", timeout: 6e4 }).trim();
|
|
52
|
+
return { success: true, output: out || "Started" };
|
|
53
|
+
} catch (e) {
|
|
54
|
+
return { success: false, output: "", error: String(e) };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
return {
|
|
60
|
+
name: "docker",
|
|
61
|
+
version: "1.0.0",
|
|
62
|
+
manifest: {
|
|
63
|
+
name: "docker",
|
|
64
|
+
version: "1.0.0",
|
|
65
|
+
description: "Docker container management",
|
|
66
|
+
tags: ["docker", "container", "devops"]
|
|
67
|
+
},
|
|
68
|
+
getTools: () => tools
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "kubernetes",
|
|
74
|
+
description: "Kubernetes cluster management and pod operations",
|
|
75
|
+
tags: ["k8s", "kubernetes", "devops"],
|
|
76
|
+
create: () => {
|
|
77
|
+
const tools = [
|
|
78
|
+
{
|
|
79
|
+
name: "kubectl_get_pods",
|
|
80
|
+
description: "List Kubernetes pods",
|
|
81
|
+
usage: "[namespace=<ns>] [all=<true>]",
|
|
82
|
+
execute(_wd, args) {
|
|
83
|
+
try {
|
|
84
|
+
const ns = args.namespace ? `-n ${args.namespace}` : "";
|
|
85
|
+
const all = args.all === "true" ? "--all-namespaces" : "";
|
|
86
|
+
const out = execSync(`kubectl get pods ${ns} ${all}`, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
87
|
+
return { success: true, output: out || "No pods" };
|
|
88
|
+
} catch (e) {
|
|
89
|
+
return { success: false, output: "", error: String(e) };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "kubectl_get_services",
|
|
95
|
+
description: "List Kubernetes services",
|
|
96
|
+
usage: "[namespace=<ns>]",
|
|
97
|
+
execute(_wd, args) {
|
|
98
|
+
try {
|
|
99
|
+
const flag = args.all === "true" ? "-a" : "";
|
|
100
|
+
const out = execSync(`docker ps ${flag}`, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
101
|
+
return { success: true, output: out || "No containers" };
|
|
102
|
+
} catch (e) {
|
|
103
|
+
return { success: false, output: "", error: String(e) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "kubectl_logs",
|
|
109
|
+
description: "Show pod logs",
|
|
110
|
+
usage: "pod=<name> [container=<name>] [tail=<lines>]",
|
|
111
|
+
execute(_wd, args) {
|
|
112
|
+
try {
|
|
113
|
+
if (!args.pod) return { success: false, output: "", error: "pod name required" };
|
|
114
|
+
const container = args.container ? `-c ${args.container}` : "";
|
|
115
|
+
const tail = args.tail ? `--tail=${args.tail}` : "";
|
|
116
|
+
const out = execSync(`kubectl logs ${args.pod} ${container} ${tail}`, { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
117
|
+
return { success: true, output: out || "(empty)" };
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return { success: false, output: "", error: String(e) };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
];
|
|
124
|
+
return {
|
|
125
|
+
name: "kubernetes",
|
|
126
|
+
version: "1.0.0",
|
|
127
|
+
manifest: {
|
|
128
|
+
name: "kubernetes",
|
|
129
|
+
version: "1.0.0",
|
|
130
|
+
description: "Kubernetes cluster management",
|
|
131
|
+
tags: ["k8s", "kubernetes", "devops"]
|
|
132
|
+
},
|
|
133
|
+
getTools: () => tools
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "firebase",
|
|
139
|
+
description: "Firebase deployment and project management",
|
|
140
|
+
tags: ["firebase", "gcp", "deploy"],
|
|
141
|
+
create: () => {
|
|
142
|
+
const tools = [
|
|
143
|
+
{
|
|
144
|
+
name: "firebase_deploy",
|
|
145
|
+
description: "Deploy to Firebase",
|
|
146
|
+
usage: "[project=<id>] [only=<feature>]",
|
|
147
|
+
execute(_wd, args) {
|
|
148
|
+
try {
|
|
149
|
+
const project = args.project ? `--project ${args.project}` : "";
|
|
150
|
+
const only = args.only ? `--only ${args.only}` : "";
|
|
151
|
+
const out = execSync(`firebase deploy ${project} ${only}`, { encoding: "utf-8", timeout: 12e4 }).trim();
|
|
152
|
+
return { success: true, output: out || "Deployed" };
|
|
153
|
+
} catch (e) {
|
|
154
|
+
return { success: false, output: "", error: String(e) };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "firebase_emulators",
|
|
160
|
+
description: "Start Firebase emulators",
|
|
161
|
+
usage: "[project=<id>]",
|
|
162
|
+
execute(_wd, args) {
|
|
163
|
+
try {
|
|
164
|
+
const project = args.project ? `--project ${args.project}` : "";
|
|
165
|
+
const out = execSync(`firebase emulators:start ${project}`, { encoding: "utf-8", timeout: 3e4 }).trim();
|
|
166
|
+
return { success: true, output: out || "Emulators started" };
|
|
167
|
+
} catch (e) {
|
|
168
|
+
return { success: false, output: "", error: String(e) };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
];
|
|
173
|
+
return {
|
|
174
|
+
name: "firebase",
|
|
175
|
+
version: "1.0.0",
|
|
176
|
+
manifest: {
|
|
177
|
+
name: "firebase",
|
|
178
|
+
version: "1.0.0",
|
|
179
|
+
description: "Firebase deployment and management",
|
|
180
|
+
tags: ["firebase", "gcp", "deploy"]
|
|
181
|
+
},
|
|
182
|
+
getTools: () => tools
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: "prisma",
|
|
188
|
+
description: "Prisma ORM tools \u2014 schema management, migrations, studio",
|
|
189
|
+
tags: ["prisma", "database", "orm"],
|
|
190
|
+
create: () => {
|
|
191
|
+
const tools = [
|
|
192
|
+
{
|
|
193
|
+
name: "prisma_generate",
|
|
194
|
+
description: "Generate Prisma client",
|
|
195
|
+
usage: "[schema=<path>]",
|
|
196
|
+
execute(_wd, args) {
|
|
197
|
+
try {
|
|
198
|
+
const schema = args.schema ? `--schema=${args.schema}` : "";
|
|
199
|
+
const out = execSync(`npx prisma generate ${schema}`, { encoding: "utf-8", timeout: 3e4 }).trim();
|
|
200
|
+
return { success: true, output: out || "Generated" };
|
|
201
|
+
} catch (e) {
|
|
202
|
+
return { success: false, output: "", error: String(e) };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "prisma_migrate",
|
|
208
|
+
description: "Run Prisma migrations",
|
|
209
|
+
usage: "name=<migration_name> [schema=<path>]",
|
|
210
|
+
execute(_wd, args) {
|
|
211
|
+
try {
|
|
212
|
+
if (!args.name) return { success: false, output: "", error: "name required" };
|
|
213
|
+
const schema = args.schema ? `--schema=${args.schema}` : "";
|
|
214
|
+
const out = execSync(`npx prisma migrate dev --name "${args.name}" ${schema}`, { encoding: "utf-8", timeout: 6e4 }).trim();
|
|
215
|
+
return { success: true, output: out || "Migration created" };
|
|
216
|
+
} catch (e) {
|
|
217
|
+
return { success: false, output: "", error: String(e) };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: "prisma_studio",
|
|
223
|
+
description: "Open Prisma Studio",
|
|
224
|
+
usage: "[schema=<path>]",
|
|
225
|
+
execute() {
|
|
226
|
+
try {
|
|
227
|
+
execSync("npx prisma studio", { encoding: "utf-8", timeout: 5e3, stdio: "pipe" });
|
|
228
|
+
return { success: true, output: "Prisma Studio started" };
|
|
229
|
+
} catch (e) {
|
|
230
|
+
return { success: true, output: "Prisma Studio launching..." };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "prisma_format",
|
|
236
|
+
description: "Format Prisma schema",
|
|
237
|
+
usage: "[schema=<path>]",
|
|
238
|
+
execute(_wd, args) {
|
|
239
|
+
try {
|
|
240
|
+
const schema = args.schema ? `--schema=${args.schema}` : "";
|
|
241
|
+
const out = execSync(`npx prisma format ${schema}`, { encoding: "utf-8", timeout: 15e3 }).trim();
|
|
242
|
+
return { success: true, output: out || "Formatted" };
|
|
243
|
+
} catch (e) {
|
|
244
|
+
return { success: false, output: "", error: String(e) };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
];
|
|
249
|
+
return {
|
|
250
|
+
name: "prisma",
|
|
251
|
+
version: "1.0.0",
|
|
252
|
+
manifest: {
|
|
253
|
+
name: "prisma",
|
|
254
|
+
version: "1.0.0",
|
|
255
|
+
description: "Prisma ORM tools",
|
|
256
|
+
tags: ["prisma", "database", "orm"]
|
|
257
|
+
},
|
|
258
|
+
getTools: () => tools
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
// src/plugin/registry.ts
|
|
265
|
+
var _require = createRequire(import.meta.url);
|
|
266
|
+
var loadedPlugins = /* @__PURE__ */ new Map();
|
|
267
|
+
var toolListeners = [];
|
|
268
|
+
function onToolsChanged(cb) {
|
|
269
|
+
toolListeners.push(cb);
|
|
270
|
+
}
|
|
271
|
+
function notifyToolsChanged() {
|
|
272
|
+
const allTools = getAllPluginTools();
|
|
273
|
+
for (const cb of toolListeners) {
|
|
274
|
+
cb(allTools);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function registerPlugin(plugin) {
|
|
278
|
+
const pkg = {
|
|
279
|
+
manifest: plugin.manifest,
|
|
280
|
+
module: plugin,
|
|
281
|
+
enabled: true,
|
|
282
|
+
loadedAt: Date.now()
|
|
283
|
+
};
|
|
284
|
+
loadedPlugins.set(plugin.name, pkg);
|
|
285
|
+
notifyToolsChanged();
|
|
286
|
+
return pkg;
|
|
287
|
+
}
|
|
288
|
+
function unregisterPlugin(name) {
|
|
289
|
+
const pkg = loadedPlugins.get(name);
|
|
290
|
+
if (!pkg) return false;
|
|
291
|
+
pkg.module.onUnload?.();
|
|
292
|
+
loadedPlugins.delete(name);
|
|
293
|
+
notifyToolsChanged();
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
function getPlugin(name) {
|
|
297
|
+
return loadedPlugins.get(name);
|
|
298
|
+
}
|
|
299
|
+
function listPlugins() {
|
|
300
|
+
return Array.from(loadedPlugins.values());
|
|
301
|
+
}
|
|
302
|
+
function enablePlugin(name) {
|
|
303
|
+
const pkg = loadedPlugins.get(name);
|
|
304
|
+
if (!pkg) return false;
|
|
305
|
+
pkg.enabled = true;
|
|
306
|
+
notifyToolsChanged();
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
function disablePlugin(name) {
|
|
310
|
+
const pkg = loadedPlugins.get(name);
|
|
311
|
+
if (!pkg) return false;
|
|
312
|
+
pkg.enabled = false;
|
|
313
|
+
notifyToolsChanged();
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
function getAllPluginTools() {
|
|
317
|
+
const tools = [];
|
|
318
|
+
for (const pkg of loadedPlugins.values()) {
|
|
319
|
+
if (!pkg.enabled) continue;
|
|
320
|
+
if (pkg.module.getTools) {
|
|
321
|
+
tools.push(...pkg.module.getTools());
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return tools;
|
|
325
|
+
}
|
|
326
|
+
function loadBuiltinPlugins() {
|
|
327
|
+
const loaded = [];
|
|
328
|
+
for (const bp of builtinPlugins) {
|
|
329
|
+
if (loadedPlugins.has(bp.name)) continue;
|
|
330
|
+
try {
|
|
331
|
+
const plugin = bp.create();
|
|
332
|
+
registerPlugin(plugin);
|
|
333
|
+
loaded.push(bp.name);
|
|
334
|
+
} catch (err) {
|
|
335
|
+
console.error(`Failed to load builtin plugin "${bp.name}":`, err);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return loaded;
|
|
339
|
+
}
|
|
340
|
+
function loadExternalPlugins(rootDir) {
|
|
341
|
+
const base = rootDir || process.cwd();
|
|
342
|
+
const pluginDir = path.join(base, PLUGIN_DIR);
|
|
343
|
+
if (!fs.existsSync(pluginDir)) return [];
|
|
344
|
+
const loaded = [];
|
|
345
|
+
for (const entry of fs.readdirSync(pluginDir)) {
|
|
346
|
+
const fullPath = path.join(pluginDir, entry);
|
|
347
|
+
if (!fs.statSync(fullPath).isDirectory()) continue;
|
|
348
|
+
const manifestPath = path.join(fullPath, "plugin.json");
|
|
349
|
+
const mainPath = path.join(fullPath, "index.js");
|
|
350
|
+
if (!fs.existsSync(manifestPath) || !fs.existsSync(mainPath)) continue;
|
|
351
|
+
try {
|
|
352
|
+
const manifestRaw = fs.readFileSync(manifestPath, "utf-8");
|
|
353
|
+
const manifest = JSON.parse(manifestRaw);
|
|
354
|
+
const pluginModule = _require(mainPath);
|
|
355
|
+
pluginModule.manifest = manifest;
|
|
356
|
+
registerPlugin(pluginModule);
|
|
357
|
+
loaded.push(entry);
|
|
358
|
+
} catch (err) {
|
|
359
|
+
console.error(`Failed to load plugin "${entry}":`, err);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return loaded;
|
|
363
|
+
}
|
|
364
|
+
function formatPluginList(plugins) {
|
|
365
|
+
if (plugins.length === 0) return "No plugins loaded.";
|
|
366
|
+
const lines = ["Loaded Plugins:"];
|
|
367
|
+
for (const p of plugins) {
|
|
368
|
+
const status = p.enabled ? "\u2713" : "\u2717";
|
|
369
|
+
const tags = p.manifest.tags?.join(", ") || "";
|
|
370
|
+
lines.push(` ${status} ${p.manifest.name}@${p.manifest.version} ${p.manifest.description}${tags ? ` (${tags})` : ""}`);
|
|
371
|
+
}
|
|
372
|
+
return lines.join("\n");
|
|
373
|
+
}
|
|
374
|
+
var marketplacePlugins = [
|
|
375
|
+
{ name: "docker", version: "1.0.0", description: "Docker container management", author: "LoveCode", downloads: 1200, rating: 4.5, tags: ["docker", "devops"], installUrl: "builtin" },
|
|
376
|
+
{ name: "kubernetes", version: "1.0.0", description: "Kubernetes cluster management", author: "LoveCode", downloads: 890, rating: 4.3, tags: ["k8s", "devops"], installUrl: "builtin" },
|
|
377
|
+
{ name: "firebase", version: "1.0.0", description: "Firebase deployment and management", author: "LoveCode", downloads: 650, rating: 4.1, tags: ["firebase", "deploy"], installUrl: "builtin" },
|
|
378
|
+
{ name: "prisma", version: "1.0.0", description: "Prisma ORM tools", author: "LoveCode", downloads: 1500, rating: 4.7, tags: ["prisma", "database", "orm"], installUrl: "builtin" },
|
|
379
|
+
{ name: "eslint-fixer", version: "1.0.0", description: "Automated ESLint fix suggestions", author: "Community", downloads: 420, rating: 3.8, tags: ["linting", "code-quality"], installUrl: "npm" },
|
|
380
|
+
{ name: "prettier", version: "1.0.0", description: "Prettier code formatting integration", author: "Community", downloads: 380, rating: 4, tags: ["formatting", "code-style"], installUrl: "npm" }
|
|
381
|
+
];
|
|
382
|
+
function searchMarketplace(query) {
|
|
383
|
+
const lower = query.toLowerCase();
|
|
384
|
+
return marketplacePlugins.filter(
|
|
385
|
+
(p) => p.name.toLowerCase().includes(lower) || p.description.toLowerCase().includes(lower) || p.tags.some((t) => t.toLowerCase().includes(lower))
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
function formatMarketplace(plugins) {
|
|
389
|
+
if (plugins.length === 0) return "No plugins found.";
|
|
390
|
+
const lines = ["Plugin Marketplace:"];
|
|
391
|
+
for (const p of plugins) {
|
|
392
|
+
lines.push(` ${p.name.padEnd(20)} v${p.version.padEnd(8)} ${(p.rating + "\u2605").padEnd(6)} ${p.downloads} downloads ${p.description}`);
|
|
393
|
+
}
|
|
394
|
+
return lines.join("\n");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export {
|
|
398
|
+
onToolsChanged,
|
|
399
|
+
registerPlugin,
|
|
400
|
+
unregisterPlugin,
|
|
401
|
+
getPlugin,
|
|
402
|
+
listPlugins,
|
|
403
|
+
enablePlugin,
|
|
404
|
+
disablePlugin,
|
|
405
|
+
getAllPluginTools,
|
|
406
|
+
loadBuiltinPlugins,
|
|
407
|
+
loadExternalPlugins,
|
|
408
|
+
formatPluginList,
|
|
409
|
+
marketplacePlugins,
|
|
410
|
+
searchMarketplace,
|
|
411
|
+
formatMarketplace
|
|
412
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
abbreviateDiff,
|
|
3
|
+
cleanupMergedBranches,
|
|
4
|
+
commit,
|
|
5
|
+
createBranch,
|
|
6
|
+
deleteBranch,
|
|
7
|
+
detectConflicts,
|
|
8
|
+
formatBranches,
|
|
9
|
+
formatConflictInfo,
|
|
10
|
+
formatLog,
|
|
11
|
+
formatResolutionSuggestions,
|
|
12
|
+
formatStatus,
|
|
13
|
+
generateCommitMessage,
|
|
14
|
+
generatePRSummary,
|
|
15
|
+
getBranches,
|
|
16
|
+
getConflictFiles,
|
|
17
|
+
getConflictMarkers,
|
|
18
|
+
getCurrentBranch,
|
|
19
|
+
getDiff,
|
|
20
|
+
getFullDiff,
|
|
21
|
+
getGitRoot,
|
|
22
|
+
getLog,
|
|
23
|
+
getPRDiff,
|
|
24
|
+
getPRLog,
|
|
25
|
+
getStagedDiff,
|
|
26
|
+
getStatus,
|
|
27
|
+
getUnstagedDiff,
|
|
28
|
+
hasConflicts,
|
|
29
|
+
isGitAvailable,
|
|
30
|
+
isRepo,
|
|
31
|
+
stageAll,
|
|
32
|
+
stageFiles,
|
|
33
|
+
suggestResolutions,
|
|
34
|
+
switchBranch
|
|
35
|
+
} from "./chunk-G7VQGYJW.js";
|
|
36
|
+
export {
|
|
37
|
+
abbreviateDiff,
|
|
38
|
+
cleanupMergedBranches,
|
|
39
|
+
commit,
|
|
40
|
+
createBranch,
|
|
41
|
+
deleteBranch,
|
|
42
|
+
detectConflicts,
|
|
43
|
+
formatBranches,
|
|
44
|
+
formatConflictInfo,
|
|
45
|
+
formatLog,
|
|
46
|
+
formatResolutionSuggestions,
|
|
47
|
+
formatStatus,
|
|
48
|
+
generateCommitMessage,
|
|
49
|
+
generatePRSummary,
|
|
50
|
+
getBranches,
|
|
51
|
+
getConflictFiles,
|
|
52
|
+
getConflictMarkers,
|
|
53
|
+
getCurrentBranch,
|
|
54
|
+
getDiff,
|
|
55
|
+
getFullDiff,
|
|
56
|
+
getGitRoot,
|
|
57
|
+
getLog,
|
|
58
|
+
getPRDiff,
|
|
59
|
+
getPRLog,
|
|
60
|
+
getStagedDiff,
|
|
61
|
+
getStatus,
|
|
62
|
+
getUnstagedDiff,
|
|
63
|
+
hasConflicts,
|
|
64
|
+
isGitAvailable,
|
|
65
|
+
isRepo,
|
|
66
|
+
stageAll,
|
|
67
|
+
stageFiles,
|
|
68
|
+
suggestResolutions,
|
|
69
|
+
switchBranch
|
|
70
|
+
};
|