crewcode-plugin-cli 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 +202 -0
- package/README.md +39 -0
- package/bin/crewcode.mjs +388 -0
- package/package.json +40 -0
- package/templates/plugins/README.md +25 -0
- package/templates/plugins/browser-docs-grabber/crewcode-plugin-api.js +80 -0
- package/templates/plugins/browser-docs-grabber/crewcode.plugin.json +13 -0
- package/templates/plugins/browser-docs-grabber/panel.html +1 -0
- package/templates/plugins/browser-docs-grabber/plugin.js +1 -0
- package/templates/plugins/codebase-graph-lite/crewcode-plugin-api.js +80 -0
- package/templates/plugins/codebase-graph-lite/crewcode.plugin.json +57 -0
- package/templates/plugins/codebase-graph-lite/panel.html +53 -0
- package/templates/plugins/codebase-graph-lite/plugin.js +49 -0
- package/templates/plugins/company-agent-http-adapter/crewcode-plugin-api.js +80 -0
- package/templates/plugins/company-agent-http-adapter/crewcode.plugin.json +24 -0
- package/templates/plugins/company-agent-http-adapter/panel.html +1 -0
- package/templates/plugins/company-agent-http-adapter/plugin.js +1 -0
- package/templates/plugins/company-agent-http-adapter/server.mjs +27 -0
- package/templates/plugins/git-risk-lens/crewcode-plugin-api.js +80 -0
- package/templates/plugins/git-risk-lens/crewcode.plugin.json +13 -0
- package/templates/plugins/git-risk-lens/panel.html +1 -0
- package/templates/plugins/git-risk-lens/plugin.js +2 -0
- package/templates/plugins/github-copilot-cli-provider/crewcode-plugin-api.js +80 -0
- package/templates/plugins/github-copilot-cli-provider/crewcode.plugin.json +42 -0
- package/templates/plugins/github-copilot-cli-provider/panel.html +1 -0
- package/templates/plugins/github-copilot-cli-provider/plugin.js +1 -0
- package/templates/plugins/handoff-pack/crewcode-plugin-api.js +80 -0
- package/templates/plugins/handoff-pack/crewcode.plugin.json +72 -0
- package/templates/plugins/handoff-pack/panel.html +63 -0
- package/templates/plugins/handoff-pack/plugin.js +156 -0
- package/templates/plugins/mcp-server-template/README.md +22 -0
- package/templates/plugins/mcp-server-template/crewcode-plugin-api.js +80 -0
- package/templates/plugins/mcp-server-template/crewcode.plugin.json +30 -0
- package/templates/plugins/mcp-server-template/panel.html +25 -0
- package/templates/plugins/mcp-server-template/plugin.js +9 -0
- package/templates/plugins/mcp-server-template/server.mjs +30 -0
- package/templates/plugins/mission-ci-widget/crewcode-plugin-api.js +80 -0
- package/templates/plugins/mission-ci-widget/crewcode.plugin.json +13 -0
- package/templates/plugins/mission-ci-widget/panel.html +1 -0
- package/templates/plugins/mission-ci-widget/plugin.js +1 -0
- package/templates/plugins/mock-agent-provider/crewcode-plugin-api.js +80 -0
- package/templates/plugins/mock-agent-provider/crewcode.plugin.json +38 -0
- package/templates/plugins/mock-agent-provider/panel.html +45 -0
- package/templates/plugins/mock-agent-provider/plugin.js +3 -0
- package/templates/plugins/openai-compatible-provider/README.md +17 -0
- package/templates/plugins/openai-compatible-provider/crewcode-plugin-api.js +80 -0
- package/templates/plugins/openai-compatible-provider/crewcode.plugin.json +28 -0
- package/templates/plugins/openai-compatible-provider/panel.html +1 -0
- package/templates/plugins/openai-compatible-provider/plugin.js +4 -0
- package/templates/plugins/openai-compatible-provider/server.mjs +21 -0
- package/templates/plugins/repo-radar/crewcode-plugin-api.js +80 -0
- package/templates/plugins/repo-radar/crewcode.plugin.json +55 -0
- package/templates/plugins/repo-radar/panel.html +56 -0
- package/templates/plugins/repo-radar/plugin.js +103 -0
- package/templates/plugins/static-panel-template/README.md +19 -0
- package/templates/plugins/static-panel-template/crewcode-plugin-api.js +80 -0
- package/templates/plugins/static-panel-template/crewcode.plugin.json +44 -0
- package/templates/plugins/static-panel-template/panel.html +36 -0
- package/templates/plugins/static-panel-template/plugin.js +30 -0
- package/templates/plugins/terminal-watchdog-lite/crewcode-plugin-api.js +80 -0
- package/templates/plugins/terminal-watchdog-lite/crewcode.plugin.json +13 -0
- package/templates/plugins/terminal-watchdog-lite/panel.html +1 -0
- package/templates/plugins/terminal-watchdog-lite/plugin.js +1 -0
- package/templates/plugins/typescript-panel-template/README.md +40 -0
- package/templates/plugins/typescript-panel-template/compiled/assets/panel-ECHDDxZz.js +8 -0
- package/templates/plugins/typescript-panel-template/compiled/assets/panel-u6HzQzUF.css +1 -0
- package/templates/plugins/typescript-panel-template/compiled/src/panel.html +13 -0
- package/templates/plugins/typescript-panel-template/crewcode.plugin.json +23 -0
- package/templates/plugins/typescript-panel-template/package.json +19 -0
- package/templates/plugins/typescript-panel-template/src/crewcode-api.ts +146 -0
- package/templates/plugins/typescript-panel-template/src/main.tsx +38 -0
- package/templates/plugins/typescript-panel-template/src/panel.html +12 -0
- package/templates/plugins/typescript-panel-template/src/style.css +17 -0
- package/templates/plugins/typescript-panel-template/tsconfig.json +20 -0
- package/templates/plugins/typescript-panel-template/vite.config.ts +14 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Official CrewCode plugin browser helper. GENERATED from src/ — do not edit by hand.
|
|
2
|
+
// Runs inside sandboxed plugin iframes. It never exposes Electron APIs.
|
|
3
|
+
"use strict";
|
|
4
|
+
(() => {
|
|
5
|
+
// src/create-api.ts
|
|
6
|
+
var CREWCODE_PLUGIN_API_VERSION = "0.1";
|
|
7
|
+
var RESERVED_NETWORK_MESSAGE = "crewcode.network.fetch is reserved in plugin API v0. Use an agentProvider runtime (http/sse-http/openai-compatible/websocket) for network access.";
|
|
8
|
+
var RESERVED_SECRETS_MESSAGE = "crewcode.secrets.get is reserved in plugin API v0. Use a provider apiKeyEnv or local CLI auth instead.";
|
|
9
|
+
function createCrewCodeApi(options = {}) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const timeoutMs = (_a = options.timeoutMs) != null ? _a : 1e4;
|
|
12
|
+
const targetOrigin = (_b = options.targetOrigin) != null ? _b : "*";
|
|
13
|
+
const pending = /* @__PURE__ */ new Map();
|
|
14
|
+
const contextListeners = /* @__PURE__ */ new Set();
|
|
15
|
+
let seq = 0;
|
|
16
|
+
let latestContext = null;
|
|
17
|
+
const reportError = (err) => {
|
|
18
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
19
|
+
window.parent.postMessage({ type: "crewcode:runtimeError", message: error.message, stack: error.stack }, targetOrigin);
|
|
20
|
+
};
|
|
21
|
+
window.addEventListener("error", (event) => {
|
|
22
|
+
var _a2;
|
|
23
|
+
return reportError((_a2 = event.error) != null ? _a2 : event.message);
|
|
24
|
+
});
|
|
25
|
+
window.addEventListener("unhandledrejection", (event) => reportError(event.reason));
|
|
26
|
+
const request = (method, params) => {
|
|
27
|
+
const id = `req-${++seq}`;
|
|
28
|
+
window.parent.postMessage({ type: "crewcode:request", id, method, params }, targetOrigin);
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
pending.set(id, { resolve, reject });
|
|
31
|
+
window.setTimeout(() => {
|
|
32
|
+
if (!pending.has(id)) return;
|
|
33
|
+
pending.delete(id);
|
|
34
|
+
reject(new Error("CrewCode plugin request timed out"));
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
window.addEventListener("message", (event) => {
|
|
39
|
+
if (event.source !== window.parent) return;
|
|
40
|
+
const msg = event.data;
|
|
41
|
+
if (!msg || typeof msg !== "object") return;
|
|
42
|
+
if (msg.type === "crewcode:context") {
|
|
43
|
+
latestContext = msg;
|
|
44
|
+
for (const listener of contextListeners) listener(latestContext);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (msg.type === "crewcode:response" && typeof msg.id === "string" && pending.has(msg.id)) {
|
|
48
|
+
const callbacks = pending.get(msg.id);
|
|
49
|
+
pending.delete(msg.id);
|
|
50
|
+
if (msg.ok) callbacks.resolve(msg.result);
|
|
51
|
+
else callbacks.reject(new Error(typeof msg.error === "string" ? msg.error : "plugin request failed"));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
apiVersion: CREWCODE_PLUGIN_API_VERSION,
|
|
56
|
+
request,
|
|
57
|
+
onContext(listener) {
|
|
58
|
+
contextListeners.add(listener);
|
|
59
|
+
if (latestContext) listener(latestContext);
|
|
60
|
+
return () => contextListeners.delete(listener);
|
|
61
|
+
},
|
|
62
|
+
getContext: () => latestContext,
|
|
63
|
+
workspace: {
|
|
64
|
+
listFiles: () => request("workspace:listFiles"),
|
|
65
|
+
readFile: (sub) => request("workspace:readFile", { sub }),
|
|
66
|
+
writeFile: (sub, text) => request("workspace:writeFile", { sub, text })
|
|
67
|
+
},
|
|
68
|
+
network: {
|
|
69
|
+
fetch: () => Promise.reject(new Error(RESERVED_NETWORK_MESSAGE))
|
|
70
|
+
},
|
|
71
|
+
secrets: {
|
|
72
|
+
get: () => Promise.reject(new Error(RESERVED_SECRETS_MESSAGE))
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/browser-global.ts
|
|
78
|
+
window.createCrewCodeApi = createCrewCodeApi;
|
|
79
|
+
window.crewcode = createCrewCodeApi();
|
|
80
|
+
})();
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/crewcode.plugin.schema.json",
|
|
3
|
+
"id": "github-copilot-cli-provider",
|
|
4
|
+
"name": "GitHub Copilot CLI Provider",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Dogfoods a real exec-backed plugin agent provider using the GitHub CLI Copilot extension.",
|
|
7
|
+
"crewcode": {
|
|
8
|
+
"apiVersion": "0.1"
|
|
9
|
+
},
|
|
10
|
+
"permissions": [
|
|
11
|
+
"agent:provider",
|
|
12
|
+
"terminal:spawn"
|
|
13
|
+
],
|
|
14
|
+
"contributes": {
|
|
15
|
+
"tabs": [
|
|
16
|
+
{
|
|
17
|
+
"id": "main",
|
|
18
|
+
"title": "Copilot CLI Provider",
|
|
19
|
+
"icon": "bot",
|
|
20
|
+
"entry": "panel.html",
|
|
21
|
+
"singleton": true
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"agentProviders": [
|
|
25
|
+
{
|
|
26
|
+
"id": "copilot-cli",
|
|
27
|
+
"title": "GitHub Copilot CLI",
|
|
28
|
+
"runtime": "exec",
|
|
29
|
+
"description": "Runs gh copilot through CrewCode's brokered exec provider runtime.",
|
|
30
|
+
"command": "gh",
|
|
31
|
+
"args": [
|
|
32
|
+
"copilot",
|
|
33
|
+
"-p",
|
|
34
|
+
"{{prompt}}"
|
|
35
|
+
],
|
|
36
|
+
"models": [
|
|
37
|
+
"gpt-5.4-mini"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><title>Copilot CLI Provider</title><style>body{margin:0;background:#0f120f;color:#e5e7eb;font:13px Inter,system-ui;padding:16px}.mono,code{font-family:'JetBrains Mono',monospace;color:#8b9a91}.card{border:1px solid #1c2f2f;background:#121712;padding:12px;margin-top:12px}</style></head><body><div class="mono">dogfood plugin</div><h1>github copilot cli provider</h1><p id="ctx">waiting for context…</p><div class="card">Requires <code>gh</code> plus the GitHub Copilot CLI extension. CrewCode spawns <code>gh copilot -p "{{prompt}}"</code> without shell interpretation.</div><div class="card">Permissions: <code>agent:provider</code>, <code>terminal:spawn</code>.</div><script src="crewcode-plugin-api.js"></script><script src="plugin.js"></script></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
window.crewcode.onContext(ctx=>{document.getElementById('ctx').textContent=ctx.workspace?`${ctx.workspace.name} · ${ctx.workspace.kind}`:'no workspace'})
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Official CrewCode plugin browser helper. GENERATED from src/ — do not edit by hand.
|
|
2
|
+
// Runs inside sandboxed plugin iframes. It never exposes Electron APIs.
|
|
3
|
+
"use strict";
|
|
4
|
+
(() => {
|
|
5
|
+
// src/create-api.ts
|
|
6
|
+
var CREWCODE_PLUGIN_API_VERSION = "0.1";
|
|
7
|
+
var RESERVED_NETWORK_MESSAGE = "crewcode.network.fetch is reserved in plugin API v0. Use an agentProvider runtime (http/sse-http/openai-compatible/websocket) for network access.";
|
|
8
|
+
var RESERVED_SECRETS_MESSAGE = "crewcode.secrets.get is reserved in plugin API v0. Use a provider apiKeyEnv or local CLI auth instead.";
|
|
9
|
+
function createCrewCodeApi(options = {}) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const timeoutMs = (_a = options.timeoutMs) != null ? _a : 1e4;
|
|
12
|
+
const targetOrigin = (_b = options.targetOrigin) != null ? _b : "*";
|
|
13
|
+
const pending = /* @__PURE__ */ new Map();
|
|
14
|
+
const contextListeners = /* @__PURE__ */ new Set();
|
|
15
|
+
let seq = 0;
|
|
16
|
+
let latestContext = null;
|
|
17
|
+
const reportError = (err) => {
|
|
18
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
19
|
+
window.parent.postMessage({ type: "crewcode:runtimeError", message: error.message, stack: error.stack }, targetOrigin);
|
|
20
|
+
};
|
|
21
|
+
window.addEventListener("error", (event) => {
|
|
22
|
+
var _a2;
|
|
23
|
+
return reportError((_a2 = event.error) != null ? _a2 : event.message);
|
|
24
|
+
});
|
|
25
|
+
window.addEventListener("unhandledrejection", (event) => reportError(event.reason));
|
|
26
|
+
const request = (method, params) => {
|
|
27
|
+
const id = `req-${++seq}`;
|
|
28
|
+
window.parent.postMessage({ type: "crewcode:request", id, method, params }, targetOrigin);
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
pending.set(id, { resolve, reject });
|
|
31
|
+
window.setTimeout(() => {
|
|
32
|
+
if (!pending.has(id)) return;
|
|
33
|
+
pending.delete(id);
|
|
34
|
+
reject(new Error("CrewCode plugin request timed out"));
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
window.addEventListener("message", (event) => {
|
|
39
|
+
if (event.source !== window.parent) return;
|
|
40
|
+
const msg = event.data;
|
|
41
|
+
if (!msg || typeof msg !== "object") return;
|
|
42
|
+
if (msg.type === "crewcode:context") {
|
|
43
|
+
latestContext = msg;
|
|
44
|
+
for (const listener of contextListeners) listener(latestContext);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (msg.type === "crewcode:response" && typeof msg.id === "string" && pending.has(msg.id)) {
|
|
48
|
+
const callbacks = pending.get(msg.id);
|
|
49
|
+
pending.delete(msg.id);
|
|
50
|
+
if (msg.ok) callbacks.resolve(msg.result);
|
|
51
|
+
else callbacks.reject(new Error(typeof msg.error === "string" ? msg.error : "plugin request failed"));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
apiVersion: CREWCODE_PLUGIN_API_VERSION,
|
|
56
|
+
request,
|
|
57
|
+
onContext(listener) {
|
|
58
|
+
contextListeners.add(listener);
|
|
59
|
+
if (latestContext) listener(latestContext);
|
|
60
|
+
return () => contextListeners.delete(listener);
|
|
61
|
+
},
|
|
62
|
+
getContext: () => latestContext,
|
|
63
|
+
workspace: {
|
|
64
|
+
listFiles: () => request("workspace:listFiles"),
|
|
65
|
+
readFile: (sub) => request("workspace:readFile", { sub }),
|
|
66
|
+
writeFile: (sub, text) => request("workspace:writeFile", { sub, text })
|
|
67
|
+
},
|
|
68
|
+
network: {
|
|
69
|
+
fetch: () => Promise.reject(new Error(RESERVED_NETWORK_MESSAGE))
|
|
70
|
+
},
|
|
71
|
+
secrets: {
|
|
72
|
+
get: () => Promise.reject(new Error(RESERVED_SECRETS_MESSAGE))
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/browser-global.ts
|
|
78
|
+
window.createCrewCodeApi = createCrewCodeApi;
|
|
79
|
+
window.crewcode = createCrewCodeApi();
|
|
80
|
+
})();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/crewcode.plugin.schema.json",
|
|
3
|
+
"id": "handoff-pack",
|
|
4
|
+
"name": "Handoff Pack",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Builds a markdown handoff snapshot for agents and teams from local workspace signals.",
|
|
7
|
+
"crewcode": { "apiVersion": "0.1" },
|
|
8
|
+
"permissions": ["workspace:read", "workspace:write"],
|
|
9
|
+
"contributes": {
|
|
10
|
+
"tabs": [
|
|
11
|
+
{
|
|
12
|
+
"id": "main",
|
|
13
|
+
"title": "Handoff Pack",
|
|
14
|
+
"icon": "file-text",
|
|
15
|
+
"entry": "panel.html",
|
|
16
|
+
"singleton": true
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"sidebarPanels": [
|
|
20
|
+
{
|
|
21
|
+
"id": "brief",
|
|
22
|
+
"title": "Handoff",
|
|
23
|
+
"icon": "file-text",
|
|
24
|
+
"entry": "panel.html"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"statusItems": [
|
|
28
|
+
{
|
|
29
|
+
"id": "handoff-status",
|
|
30
|
+
"title": "Handoff Pack",
|
|
31
|
+
"text": "handoff",
|
|
32
|
+
"icon": "file-text",
|
|
33
|
+
"sidebarPanel": "brief"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"commands": [
|
|
37
|
+
{
|
|
38
|
+
"id": "open-handoff",
|
|
39
|
+
"title": "Open Handoff Pack",
|
|
40
|
+
"icon": "file-text",
|
|
41
|
+
"group": "Plugins"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"chatActions": [
|
|
45
|
+
{
|
|
46
|
+
"id": "handoff-from-chat",
|
|
47
|
+
"title": "Open Handoff Pack",
|
|
48
|
+
"icon": "file-text",
|
|
49
|
+
"sidebarPanel": "brief",
|
|
50
|
+
"messageRole": "any"
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"chatHeaderItems": [
|
|
54
|
+
{
|
|
55
|
+
"id": "handoff-header",
|
|
56
|
+
"title": "Open Handoff Pack",
|
|
57
|
+
"text": "handoff",
|
|
58
|
+
"icon": "file-text",
|
|
59
|
+
"sidebarPanel": "brief"
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"editorActions": [
|
|
63
|
+
{
|
|
64
|
+
"id": "handoff-current-file",
|
|
65
|
+
"title": "Add current context to Handoff Pack",
|
|
66
|
+
"icon": "file-text",
|
|
67
|
+
"sidebarPanel": "brief",
|
|
68
|
+
"filePattern": "*"
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Handoff Pack</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: dark; --bg:#0f120f; --panel:#121712; --border:#1c2f2f; --text:#e5e7eb; --muted:#8b9a91; --accent:#285a48; --danger:#fca5a5; --mono:'JetBrains Mono', ui-monospace, monospace; --sans:Inter, ui-sans-serif, system-ui, sans-serif; }
|
|
9
|
+
* { box-sizing:border-box; }
|
|
10
|
+
body { margin:0; background:var(--bg); color:var(--text); font-family:var(--sans); font-size:13px; }
|
|
11
|
+
header { padding:16px; border-bottom:1px solid var(--border); display:flex; justify-content:space-between; gap:12px; align-items:flex-start; }
|
|
12
|
+
h1 { margin:0; font-size:15px; font-weight:650; letter-spacing:-0.02em; }
|
|
13
|
+
p { margin:4px 0 0; color:var(--muted); line-height:1.45; }
|
|
14
|
+
button { background:var(--accent); color:#e6f0eb; border:1px solid #356b57; padding:7px 10px; border-radius:0; font-family:var(--mono); font-size:11px; cursor:pointer; }
|
|
15
|
+
button.secondary { background:transparent; border-color:var(--border); color:var(--muted); }
|
|
16
|
+
button.danger { background:transparent; border-color:#7f1d1d; color:var(--danger); }
|
|
17
|
+
main { padding:14px; display:grid; gap:12px; grid-template-columns: minmax(260px, 420px) minmax(320px, 1fr); }
|
|
18
|
+
.card { border:1px solid var(--border); background:var(--panel); padding:10px; }
|
|
19
|
+
.card h2 { margin:0 0 8px; font-size:12px; font-family:var(--mono); font-weight:500; color:#c8d8d0; }
|
|
20
|
+
label { display:grid; gap:5px; color:var(--muted); font-family:var(--mono); font-size:11px; margin-bottom:9px; }
|
|
21
|
+
input, textarea { width:100%; background:#0b0f0c; color:var(--text); border:1px solid var(--border); padding:8px; border-radius:0; font:12px var(--mono); }
|
|
22
|
+
textarea { min-height:96px; resize:vertical; }
|
|
23
|
+
pre { margin:0; min-height:420px; max-height:70vh; overflow:auto; white-space:pre-wrap; background:#0b0f0c; border:1px solid var(--border); padding:12px; font:12px/1.5 var(--mono); color:#d9e6df; }
|
|
24
|
+
.row { display:flex; gap:8px; flex-wrap:wrap; }
|
|
25
|
+
.eyebrow, .status { font-family:var(--mono); font-size:10px; color:var(--muted); text-transform:lowercase; }
|
|
26
|
+
.status { margin-top:8px; text-transform:none; }
|
|
27
|
+
.error { color:var(--danger); }
|
|
28
|
+
@media (max-width: 860px) { main { grid-template-columns:1fr; } header { display:grid; } }
|
|
29
|
+
</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
<header>
|
|
33
|
+
<div>
|
|
34
|
+
<div class="eyebrow">dogfood plugin</div>
|
|
35
|
+
<h1>handoff pack</h1>
|
|
36
|
+
<p id="workspace">waiting for CrewCode context…</p>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="row">
|
|
39
|
+
<button id="generate">generate</button>
|
|
40
|
+
<button id="save">save markdown</button>
|
|
41
|
+
</div>
|
|
42
|
+
</header>
|
|
43
|
+
<main>
|
|
44
|
+
<section class="card">
|
|
45
|
+
<h2>brief inputs</h2>
|
|
46
|
+
<label>objective <input id="objective" value="Prepare a concise handoff for the next agent or teammate." /></label>
|
|
47
|
+
<label>notes <textarea id="notes" placeholder="Add decisions, blockers, links, or context you want preserved."></textarea></label>
|
|
48
|
+
<label>focus paths <textarea id="focus" placeholder="src/main/plugins.ts docs/plugins.md"></textarea></label>
|
|
49
|
+
<div class="row">
|
|
50
|
+
<button id="copy" class="secondary">copy preview</button>
|
|
51
|
+
<button id="clear" class="danger">clear</button>
|
|
52
|
+
</div>
|
|
53
|
+
<div id="status" class="status">generates from README/package/docs plus selected focus paths.</div>
|
|
54
|
+
</section>
|
|
55
|
+
<section class="card">
|
|
56
|
+
<h2>markdown preview</h2>
|
|
57
|
+
<pre id="preview">click generate to build a handoff pack.</pre>
|
|
58
|
+
</section>
|
|
59
|
+
</main>
|
|
60
|
+
<script src="crewcode-plugin-api.js"></script>
|
|
61
|
+
<script src="plugin.js"></script>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
const els = {
|
|
2
|
+
workspace: document.getElementById('workspace'),
|
|
3
|
+
objective: document.getElementById('objective'),
|
|
4
|
+
notes: document.getElementById('notes'),
|
|
5
|
+
focus: document.getElementById('focus'),
|
|
6
|
+
preview: document.getElementById('preview'),
|
|
7
|
+
status: document.getElementById('status'),
|
|
8
|
+
generate: document.getElementById('generate'),
|
|
9
|
+
save: document.getElementById('save'),
|
|
10
|
+
copy: document.getElementById('copy'),
|
|
11
|
+
clear: document.getElementById('clear'),
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let latestContext = null
|
|
15
|
+
let latestMarkdown = ''
|
|
16
|
+
|
|
17
|
+
function setStatus(message, danger = false) {
|
|
18
|
+
els.status.textContent = message
|
|
19
|
+
els.status.className = danger ? 'status error' : 'status'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function todayStamp() {
|
|
23
|
+
return new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function escapeMd(value) {
|
|
27
|
+
return String(value || '').trim()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function readOptional(path) {
|
|
31
|
+
try {
|
|
32
|
+
const { text } = await window.crewcode.workspace.readFile(path)
|
|
33
|
+
return text
|
|
34
|
+
} catch {
|
|
35
|
+
return ''
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function summarizePackageJson(text) {
|
|
40
|
+
if (!text) return '- package metadata unavailable'
|
|
41
|
+
try {
|
|
42
|
+
const pkg = JSON.parse(text)
|
|
43
|
+
const scripts = pkg.scripts ? Object.keys(pkg.scripts).slice(0, 12).join(', ') : 'none'
|
|
44
|
+
const deps = Object.keys({ ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }).slice(0, 16).join(', ')
|
|
45
|
+
return [`- package: ${pkg.name || 'unknown'}${pkg.version ? `@${pkg.version}` : ''}`, `- scripts: ${scripts}`, `- notable deps: ${deps || 'none listed'}`].join('\n')
|
|
46
|
+
} catch {
|
|
47
|
+
return '- package metadata could not be parsed'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function fileStats(files) {
|
|
52
|
+
const source = files.filter(file => /\.(ts|tsx|js|jsx|css|html|md|json|py|go|rs)$/i.test(file))
|
|
53
|
+
const docs = files.filter(file => /(^|\/)(readme|docs\/|.*\.md$)/i.test(file))
|
|
54
|
+
const tests = files.filter(file => /(test|spec)\.(ts|tsx|js|jsx)$/i.test(file))
|
|
55
|
+
return [`- total files: ${files.length}`, `- source-like files: ${source.length}`, `- docs/markdown files: ${docs.length}`, `- test files: ${tests.length}`].join('\n')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function generate() {
|
|
59
|
+
els.generate.disabled = true
|
|
60
|
+
els.generate.textContent = 'generating…'
|
|
61
|
+
try {
|
|
62
|
+
const ctx = latestContext
|
|
63
|
+
const { files } = await window.crewcode.workspace.listFiles()
|
|
64
|
+
const readmePath = files.find(file => /^readme\.md$/i.test(file)) || files.find(file => /(^|\/)readme\.md$/i.test(file))
|
|
65
|
+
const packagePath = files.includes('package.json') ? 'package.json' : ''
|
|
66
|
+
const readme = readmePath ? await readOptional(readmePath) : ''
|
|
67
|
+
const pkg = packagePath ? await readOptional(packagePath) : ''
|
|
68
|
+
const focusPaths = els.focus.value.split(/\r?\n/).map(s => s.trim()).filter(Boolean).slice(0, 8)
|
|
69
|
+
const focusBlocks = []
|
|
70
|
+
|
|
71
|
+
for (const path of focusPaths) {
|
|
72
|
+
const text = await readOptional(path)
|
|
73
|
+
focusBlocks.push(text
|
|
74
|
+
? `### ${path}\n\n\`\`\`\n${text.slice(0, 4000)}\n\`\`\``
|
|
75
|
+
: `### ${path}\n\n_unavailable or too large_`)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
latestMarkdown = [
|
|
79
|
+
'# Handoff Pack',
|
|
80
|
+
'',
|
|
81
|
+
`Generated: ${new Date().toLocaleString()}`,
|
|
82
|
+
`Workspace: ${ctx?.workspace?.name || 'unknown'} (${ctx?.workspace?.kind || 'unknown'})`,
|
|
83
|
+
'',
|
|
84
|
+
'## Objective',
|
|
85
|
+
'',
|
|
86
|
+
escapeMd(els.objective.value) || '_not specified_',
|
|
87
|
+
'',
|
|
88
|
+
'## Human notes',
|
|
89
|
+
'',
|
|
90
|
+
escapeMd(els.notes.value) || '_none provided_',
|
|
91
|
+
'',
|
|
92
|
+
'## Workspace snapshot',
|
|
93
|
+
'',
|
|
94
|
+
fileStats(files),
|
|
95
|
+
'',
|
|
96
|
+
'## Package metadata',
|
|
97
|
+
'',
|
|
98
|
+
summarizePackageJson(pkg),
|
|
99
|
+
'',
|
|
100
|
+
'## README excerpt',
|
|
101
|
+
'',
|
|
102
|
+
readme ? readme.slice(0, 2200) : '_README not found_',
|
|
103
|
+
'',
|
|
104
|
+
'## Focus files',
|
|
105
|
+
'',
|
|
106
|
+
focusBlocks.length ? focusBlocks.join('\n\n') : '_no focus paths provided_',
|
|
107
|
+
'',
|
|
108
|
+
'## Suggested next-agent checklist',
|
|
109
|
+
'',
|
|
110
|
+
'- Confirm current branch and pending changes.',
|
|
111
|
+
'- Re-run relevant validation before editing broadly.',
|
|
112
|
+
'- Preserve plugin security boundaries and remote workspace behavior.',
|
|
113
|
+
'- Update docs if public plugin/API behavior changes.',
|
|
114
|
+
'',
|
|
115
|
+
].join('\n')
|
|
116
|
+
|
|
117
|
+
els.preview.textContent = latestMarkdown
|
|
118
|
+
setStatus('handoff preview generated')
|
|
119
|
+
} catch (err) {
|
|
120
|
+
setStatus(err.message || 'failed to generate handoff', true)
|
|
121
|
+
} finally {
|
|
122
|
+
els.generate.disabled = false
|
|
123
|
+
els.generate.textContent = 'generate'
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function save() {
|
|
128
|
+
if (!latestMarkdown) await generate()
|
|
129
|
+
const path = `.crewcode/handoffs/handoff-${todayStamp()}.md`
|
|
130
|
+
try {
|
|
131
|
+
await window.crewcode.workspace.writeFile(path, latestMarkdown)
|
|
132
|
+
setStatus(`saved ${path}`)
|
|
133
|
+
} catch (err) {
|
|
134
|
+
setStatus(err.message || 'failed to save handoff', true)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
window.crewcode.onContext(ctx => {
|
|
139
|
+
latestContext = ctx
|
|
140
|
+
els.workspace.textContent = ctx.workspace ? `${ctx.workspace.name} · ${ctx.workspace.kind}` : 'no active workspace'
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
els.generate.addEventListener('click', generate)
|
|
144
|
+
els.save.addEventListener('click', save)
|
|
145
|
+
els.copy.addEventListener('click', async () => {
|
|
146
|
+
if (!latestMarkdown) await generate()
|
|
147
|
+
await navigator.clipboard.writeText(latestMarkdown)
|
|
148
|
+
setStatus('copied preview to clipboard')
|
|
149
|
+
})
|
|
150
|
+
els.clear.addEventListener('click', () => {
|
|
151
|
+
els.notes.value = ''
|
|
152
|
+
els.focus.value = ''
|
|
153
|
+
latestMarkdown = ''
|
|
154
|
+
els.preview.textContent = 'click generate to build a handoff pack.'
|
|
155
|
+
setStatus('cleared local draft')
|
|
156
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# MCP server plugin template
|
|
2
|
+
|
|
3
|
+
A starter layout for plugins that declare a local MCP server.
|
|
4
|
+
|
|
5
|
+
## What it demonstrates
|
|
6
|
+
|
|
7
|
+
- `mcp:server` permission and `contributes.mcpServers` manifest shape.
|
|
8
|
+
- A companion tab panel explaining the server contribution.
|
|
9
|
+
- A tiny stdio `server.mjs` skeleton for authors to replace with a real MCP SDK server.
|
|
10
|
+
|
|
11
|
+
## Install locally
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
mkdir -p ~/.crewcode/plugins/mcp-server-template
|
|
15
|
+
cp -R . ~/.crewcode/plugins/mcp-server-template
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Refresh the Plugins page, approve the plugin, and enable its MCP server from CrewCode's MCP picker when available.
|
|
19
|
+
|
|
20
|
+
## Notes
|
|
21
|
+
|
|
22
|
+
The skeleton intentionally does not request network or secrets. Add those only after CrewCode's audited gates support your use case.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Official CrewCode plugin browser helper. GENERATED from src/ — do not edit by hand.
|
|
2
|
+
// Runs inside sandboxed plugin iframes. It never exposes Electron APIs.
|
|
3
|
+
"use strict";
|
|
4
|
+
(() => {
|
|
5
|
+
// src/create-api.ts
|
|
6
|
+
var CREWCODE_PLUGIN_API_VERSION = "0.1";
|
|
7
|
+
var RESERVED_NETWORK_MESSAGE = "crewcode.network.fetch is reserved in plugin API v0. Use an agentProvider runtime (http/sse-http/openai-compatible/websocket) for network access.";
|
|
8
|
+
var RESERVED_SECRETS_MESSAGE = "crewcode.secrets.get is reserved in plugin API v0. Use a provider apiKeyEnv or local CLI auth instead.";
|
|
9
|
+
function createCrewCodeApi(options = {}) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const timeoutMs = (_a = options.timeoutMs) != null ? _a : 1e4;
|
|
12
|
+
const targetOrigin = (_b = options.targetOrigin) != null ? _b : "*";
|
|
13
|
+
const pending = /* @__PURE__ */ new Map();
|
|
14
|
+
const contextListeners = /* @__PURE__ */ new Set();
|
|
15
|
+
let seq = 0;
|
|
16
|
+
let latestContext = null;
|
|
17
|
+
const reportError = (err) => {
|
|
18
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
19
|
+
window.parent.postMessage({ type: "crewcode:runtimeError", message: error.message, stack: error.stack }, targetOrigin);
|
|
20
|
+
};
|
|
21
|
+
window.addEventListener("error", (event) => {
|
|
22
|
+
var _a2;
|
|
23
|
+
return reportError((_a2 = event.error) != null ? _a2 : event.message);
|
|
24
|
+
});
|
|
25
|
+
window.addEventListener("unhandledrejection", (event) => reportError(event.reason));
|
|
26
|
+
const request = (method, params) => {
|
|
27
|
+
const id = `req-${++seq}`;
|
|
28
|
+
window.parent.postMessage({ type: "crewcode:request", id, method, params }, targetOrigin);
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
pending.set(id, { resolve, reject });
|
|
31
|
+
window.setTimeout(() => {
|
|
32
|
+
if (!pending.has(id)) return;
|
|
33
|
+
pending.delete(id);
|
|
34
|
+
reject(new Error("CrewCode plugin request timed out"));
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
window.addEventListener("message", (event) => {
|
|
39
|
+
if (event.source !== window.parent) return;
|
|
40
|
+
const msg = event.data;
|
|
41
|
+
if (!msg || typeof msg !== "object") return;
|
|
42
|
+
if (msg.type === "crewcode:context") {
|
|
43
|
+
latestContext = msg;
|
|
44
|
+
for (const listener of contextListeners) listener(latestContext);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (msg.type === "crewcode:response" && typeof msg.id === "string" && pending.has(msg.id)) {
|
|
48
|
+
const callbacks = pending.get(msg.id);
|
|
49
|
+
pending.delete(msg.id);
|
|
50
|
+
if (msg.ok) callbacks.resolve(msg.result);
|
|
51
|
+
else callbacks.reject(new Error(typeof msg.error === "string" ? msg.error : "plugin request failed"));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
apiVersion: CREWCODE_PLUGIN_API_VERSION,
|
|
56
|
+
request,
|
|
57
|
+
onContext(listener) {
|
|
58
|
+
contextListeners.add(listener);
|
|
59
|
+
if (latestContext) listener(latestContext);
|
|
60
|
+
return () => contextListeners.delete(listener);
|
|
61
|
+
},
|
|
62
|
+
getContext: () => latestContext,
|
|
63
|
+
workspace: {
|
|
64
|
+
listFiles: () => request("workspace:listFiles"),
|
|
65
|
+
readFile: (sub) => request("workspace:readFile", { sub }),
|
|
66
|
+
writeFile: (sub, text) => request("workspace:writeFile", { sub, text })
|
|
67
|
+
},
|
|
68
|
+
network: {
|
|
69
|
+
fetch: () => Promise.reject(new Error(RESERVED_NETWORK_MESSAGE))
|
|
70
|
+
},
|
|
71
|
+
secrets: {
|
|
72
|
+
get: () => Promise.reject(new Error(RESERVED_SECRETS_MESSAGE))
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/browser-global.ts
|
|
78
|
+
window.createCrewCodeApi = createCrewCodeApi;
|
|
79
|
+
window.crewcode = createCrewCodeApi();
|
|
80
|
+
})();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../schemas/crewcode.plugin.schema.json",
|
|
3
|
+
"id": "mcp-server-template",
|
|
4
|
+
"name": "MCP Server Template",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Starter template for declaring a local MCP server contribution from a CrewCode plugin.",
|
|
7
|
+
"crewcode": { "apiVersion": "0.1" },
|
|
8
|
+
"permissions": ["mcp:server", "workspace:read"],
|
|
9
|
+
"contributes": {
|
|
10
|
+
"tabs": [
|
|
11
|
+
{
|
|
12
|
+
"id": "main",
|
|
13
|
+
"title": "MCP Template",
|
|
14
|
+
"icon": "terminal",
|
|
15
|
+
"entry": "panel.html",
|
|
16
|
+
"singleton": true
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"mcpServers": [
|
|
20
|
+
{
|
|
21
|
+
"id": "local-context",
|
|
22
|
+
"title": "Local Context MCP",
|
|
23
|
+
"command": "node",
|
|
24
|
+
"args": ["server.mjs"],
|
|
25
|
+
"category": "context",
|
|
26
|
+
"description": "Example stdio MCP server. Replace server.mjs with your tool implementation."
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>MCP Server Template</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { margin: 0; background: #0f120f; color: #e6f0eb; font: 13px Inter, system-ui, sans-serif; padding: 16px; }
|
|
8
|
+
h1 { margin: 6px 0 8px; font-size: 22px; }
|
|
9
|
+
p { color: #8b9a91; line-height: 1.5; }
|
|
10
|
+
code, pre, .mono { font-family: 'JetBrains Mono', monospace; color: #9fbfaf; }
|
|
11
|
+
.panel { border: 1px solid #1c2f2f; background: #121712; padding: 12px; margin-top: 12px; }
|
|
12
|
+
</style>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div class="mono">crewcode plugin template / mcp server</div>
|
|
16
|
+
<h1>local context MCP</h1>
|
|
17
|
+
<p>This plugin declares an MCP server contribution. CrewCode owns enablement and spawning; the plugin UI is only a companion panel.</p>
|
|
18
|
+
<pre class="panel">contributes.mcpServers[0]
|
|
19
|
+
command: node
|
|
20
|
+
args: server.mjs</pre>
|
|
21
|
+
<pre id="ctx" class="panel">waiting for context…</pre>
|
|
22
|
+
<script src="crewcode-plugin-api.js"></script>
|
|
23
|
+
<script src="plugin.js"></script>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|