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.
Files changed (75) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +39 -0
  3. package/bin/crewcode.mjs +388 -0
  4. package/package.json +40 -0
  5. package/templates/plugins/README.md +25 -0
  6. package/templates/plugins/browser-docs-grabber/crewcode-plugin-api.js +80 -0
  7. package/templates/plugins/browser-docs-grabber/crewcode.plugin.json +13 -0
  8. package/templates/plugins/browser-docs-grabber/panel.html +1 -0
  9. package/templates/plugins/browser-docs-grabber/plugin.js +1 -0
  10. package/templates/plugins/codebase-graph-lite/crewcode-plugin-api.js +80 -0
  11. package/templates/plugins/codebase-graph-lite/crewcode.plugin.json +57 -0
  12. package/templates/plugins/codebase-graph-lite/panel.html +53 -0
  13. package/templates/plugins/codebase-graph-lite/plugin.js +49 -0
  14. package/templates/plugins/company-agent-http-adapter/crewcode-plugin-api.js +80 -0
  15. package/templates/plugins/company-agent-http-adapter/crewcode.plugin.json +24 -0
  16. package/templates/plugins/company-agent-http-adapter/panel.html +1 -0
  17. package/templates/plugins/company-agent-http-adapter/plugin.js +1 -0
  18. package/templates/plugins/company-agent-http-adapter/server.mjs +27 -0
  19. package/templates/plugins/git-risk-lens/crewcode-plugin-api.js +80 -0
  20. package/templates/plugins/git-risk-lens/crewcode.plugin.json +13 -0
  21. package/templates/plugins/git-risk-lens/panel.html +1 -0
  22. package/templates/plugins/git-risk-lens/plugin.js +2 -0
  23. package/templates/plugins/github-copilot-cli-provider/crewcode-plugin-api.js +80 -0
  24. package/templates/plugins/github-copilot-cli-provider/crewcode.plugin.json +42 -0
  25. package/templates/plugins/github-copilot-cli-provider/panel.html +1 -0
  26. package/templates/plugins/github-copilot-cli-provider/plugin.js +1 -0
  27. package/templates/plugins/handoff-pack/crewcode-plugin-api.js +80 -0
  28. package/templates/plugins/handoff-pack/crewcode.plugin.json +72 -0
  29. package/templates/plugins/handoff-pack/panel.html +63 -0
  30. package/templates/plugins/handoff-pack/plugin.js +156 -0
  31. package/templates/plugins/mcp-server-template/README.md +22 -0
  32. package/templates/plugins/mcp-server-template/crewcode-plugin-api.js +80 -0
  33. package/templates/plugins/mcp-server-template/crewcode.plugin.json +30 -0
  34. package/templates/plugins/mcp-server-template/panel.html +25 -0
  35. package/templates/plugins/mcp-server-template/plugin.js +9 -0
  36. package/templates/plugins/mcp-server-template/server.mjs +30 -0
  37. package/templates/plugins/mission-ci-widget/crewcode-plugin-api.js +80 -0
  38. package/templates/plugins/mission-ci-widget/crewcode.plugin.json +13 -0
  39. package/templates/plugins/mission-ci-widget/panel.html +1 -0
  40. package/templates/plugins/mission-ci-widget/plugin.js +1 -0
  41. package/templates/plugins/mock-agent-provider/crewcode-plugin-api.js +80 -0
  42. package/templates/plugins/mock-agent-provider/crewcode.plugin.json +38 -0
  43. package/templates/plugins/mock-agent-provider/panel.html +45 -0
  44. package/templates/plugins/mock-agent-provider/plugin.js +3 -0
  45. package/templates/plugins/openai-compatible-provider/README.md +17 -0
  46. package/templates/plugins/openai-compatible-provider/crewcode-plugin-api.js +80 -0
  47. package/templates/plugins/openai-compatible-provider/crewcode.plugin.json +28 -0
  48. package/templates/plugins/openai-compatible-provider/panel.html +1 -0
  49. package/templates/plugins/openai-compatible-provider/plugin.js +4 -0
  50. package/templates/plugins/openai-compatible-provider/server.mjs +21 -0
  51. package/templates/plugins/repo-radar/crewcode-plugin-api.js +80 -0
  52. package/templates/plugins/repo-radar/crewcode.plugin.json +55 -0
  53. package/templates/plugins/repo-radar/panel.html +56 -0
  54. package/templates/plugins/repo-radar/plugin.js +103 -0
  55. package/templates/plugins/static-panel-template/README.md +19 -0
  56. package/templates/plugins/static-panel-template/crewcode-plugin-api.js +80 -0
  57. package/templates/plugins/static-panel-template/crewcode.plugin.json +44 -0
  58. package/templates/plugins/static-panel-template/panel.html +36 -0
  59. package/templates/plugins/static-panel-template/plugin.js +30 -0
  60. package/templates/plugins/terminal-watchdog-lite/crewcode-plugin-api.js +80 -0
  61. package/templates/plugins/terminal-watchdog-lite/crewcode.plugin.json +13 -0
  62. package/templates/plugins/terminal-watchdog-lite/panel.html +1 -0
  63. package/templates/plugins/terminal-watchdog-lite/plugin.js +1 -0
  64. package/templates/plugins/typescript-panel-template/README.md +40 -0
  65. package/templates/plugins/typescript-panel-template/compiled/assets/panel-ECHDDxZz.js +8 -0
  66. package/templates/plugins/typescript-panel-template/compiled/assets/panel-u6HzQzUF.css +1 -0
  67. package/templates/plugins/typescript-panel-template/compiled/src/panel.html +13 -0
  68. package/templates/plugins/typescript-panel-template/crewcode.plugin.json +23 -0
  69. package/templates/plugins/typescript-panel-template/package.json +19 -0
  70. package/templates/plugins/typescript-panel-template/src/crewcode-api.ts +146 -0
  71. package/templates/plugins/typescript-panel-template/src/main.tsx +38 -0
  72. package/templates/plugins/typescript-panel-template/src/panel.html +12 -0
  73. package/templates/plugins/typescript-panel-template/src/style.css +17 -0
  74. package/templates/plugins/typescript-panel-template/tsconfig.json +20 -0
  75. package/templates/plugins/typescript-panel-template/vite.config.ts +14 -0
@@ -0,0 +1,56 @@
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>Repo Radar</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
+ main { padding:14px; display:grid; gap:12px; }
17
+ .grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; }
18
+ .stat, .section { border:1px solid var(--border); background:var(--panel); padding:10px; }
19
+ .stat .n { font-family:var(--mono); font-size:20px; color:#dff3e9; }
20
+ .stat .l, .eyebrow { font-family:var(--mono); font-size:10px; color:var(--muted); text-transform:lowercase; }
21
+ .section h2 { margin:0 0 8px; font-size:12px; font-family:var(--mono); font-weight:500; color:#c8d8d0; }
22
+ ul { margin:0; padding:0; list-style:none; display:grid; gap:6px; }
23
+ li { border-top:1px solid var(--border); padding-top:6px; color:#cdd7d1; }
24
+ code { font-family:var(--mono); color:#dff3e9; font-size:11px; }
25
+ .line { color:var(--muted); font-family:var(--mono); font-size:11px; }
26
+ .danger { color:var(--danger); }
27
+ .empty { color:var(--muted); font-family:var(--mono); }
28
+ @media (max-width: 640px) { .grid { 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>repo radar</h1>
36
+ <p id="workspace">waiting for CrewCode context…</p>
37
+ </div>
38
+ <div style="display:flex; gap:8px; flex-wrap:wrap; justify-content:flex-end">
39
+ <button id="scan">scan workspace</button>
40
+ <button id="clear" class="secondary">clear</button>
41
+ </div>
42
+ </header>
43
+ <main>
44
+ <div class="grid">
45
+ <div class="stat"><div class="n" id="filesCount">—</div><div class="l">files scanned</div></div>
46
+ <div class="stat"><div class="n" id="todoCount">—</div><div class="l">todos found</div></div>
47
+ <div class="stat"><div class="n" id="riskCount">—</div><div class="l">risk signals</div></div>
48
+ </div>
49
+ <section class="section"><h2>todo / fixme / hack</h2><ul id="todos"><li class="empty">run a scan to inspect source comments</li></ul></section>
50
+ <section class="section"><h2>risk signals</h2><ul id="risks"><li class="empty">looks for env files, lockfiles, generated dirs, large source files</li></ul></section>
51
+ <section class="section"><h2>source mix</h2><ul id="mix"><li class="empty">file extension summary appears here</li></ul></section>
52
+ </main>
53
+ <script src="crewcode-plugin-api.js"></script>
54
+ <script src="plugin.js"></script>
55
+ </body>
56
+ </html>
@@ -0,0 +1,103 @@
1
+ const els = {
2
+ workspace: document.getElementById('workspace'),
3
+ filesCount: document.getElementById('filesCount'),
4
+ todoCount: document.getElementById('todoCount'),
5
+ riskCount: document.getElementById('riskCount'),
6
+ todos: document.getElementById('todos'),
7
+ risks: document.getElementById('risks'),
8
+ mix: document.getElementById('mix'),
9
+ scan: document.getElementById('scan'),
10
+ clear: document.getElementById('clear'),
11
+ }
12
+
13
+ const TEXT_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|json|md|css|html|py|go|rs|java|kt|swift|rb|php|yml|yaml|toml)$/i
14
+ const SKIP = /(^|\/)(node_modules|\.git|dist|out|build|\.next|coverage)(\/|$)/
15
+ const TODO_RE = /\b(todo|fixme|hack|xxx)\b/i
16
+
17
+ function setList(el, items, render, empty) {
18
+ el.innerHTML = ''
19
+ if (!items.length) {
20
+ const li = document.createElement('li')
21
+ li.className = 'empty'
22
+ li.textContent = empty
23
+ el.appendChild(li)
24
+ return
25
+ }
26
+ for (const item of items) {
27
+ const li = document.createElement('li')
28
+ li.innerHTML = render(item)
29
+ el.appendChild(li)
30
+ }
31
+ }
32
+
33
+ function extOf(file) {
34
+ const match = file.match(/\.([^.\/]+)$/)
35
+ return match ? match[1].toLowerCase() : 'none'
36
+ }
37
+
38
+ function riskSignals(files) {
39
+ const out = []
40
+ for (const file of files) {
41
+ if (/(^|\/)\.env(\.|$)/.test(file)) out.push({ file, reason: 'environment file' })
42
+ if (/package-lock\.json$|pnpm-lock\.yaml$|yarn\.lock$/.test(file)) out.push({ file, reason: 'lockfile changed often' })
43
+ if (/(^|\/)(generated|vendor)\//.test(file)) out.push({ file, reason: 'generated/vendor path' })
44
+ if (/\.(pem|key|p12|pfx)$/i.test(file)) out.push({ file, reason: 'secret-like file extension' })
45
+ }
46
+ return out.slice(0, 12)
47
+ }
48
+
49
+ async function scan() {
50
+ els.scan.disabled = true
51
+ els.scan.textContent = 'scanning…'
52
+ try {
53
+ const { files } = await window.crewcode.workspace.listFiles()
54
+ const sourceFiles = files.filter(file => TEXT_EXT.test(file) && !SKIP.test(file)).slice(0, 120)
55
+ const todos = []
56
+ const extensions = new Map()
57
+
58
+ for (const file of files) extensions.set(extOf(file), (extensions.get(extOf(file)) || 0) + 1)
59
+
60
+ for (const file of sourceFiles) {
61
+ try {
62
+ const { text } = await window.crewcode.workspace.readFile(file)
63
+ const lines = text.split(/\r?\n/)
64
+ lines.forEach((line, index) => {
65
+ if (TODO_RE.test(line) && todos.length < 20) todos.push({ file, line: index + 1, text: line.trim().slice(0, 180) })
66
+ })
67
+ } catch {
68
+ // Some files can be too large or disappear during a scan; keep radar resilient.
69
+ }
70
+ }
71
+
72
+ const risks = riskSignals(files)
73
+ const mix = [...extensions.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12).map(([ext, count]) => ({ ext, count }))
74
+
75
+ els.filesCount.textContent = String(files.length)
76
+ els.todoCount.textContent = String(todos.length)
77
+ els.riskCount.textContent = String(risks.length)
78
+ setList(els.todos, todos, item => `<code>${item.file}:${item.line}</code><div class="line">${escapeHtml(item.text)}</div>`, 'no todo-style comments found')
79
+ setList(els.risks, risks, item => `<code class="danger">${item.reason}</code><div class="line">${escapeHtml(item.file)}</div>`, 'no obvious risk signals found')
80
+ setList(els.mix, mix, item => `<code>.${escapeHtml(item.ext)}</code><div class="line">${item.count} file${item.count === 1 ? '' : 's'}</div>`, 'no files found')
81
+ } finally {
82
+ els.scan.disabled = false
83
+ els.scan.textContent = 'scan workspace'
84
+ }
85
+ }
86
+
87
+ function escapeHtml(value) {
88
+ return String(value).replace(/[&<>"]/g, char => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[char]))
89
+ }
90
+
91
+ window.crewcode.onContext(ctx => {
92
+ els.workspace.textContent = ctx.workspace ? `${ctx.workspace.name} · ${ctx.workspace.kind}` : 'no active workspace'
93
+ })
94
+
95
+ els.scan.addEventListener('click', scan)
96
+ els.clear.addEventListener('click', () => {
97
+ els.filesCount.textContent = '—'
98
+ els.todoCount.textContent = '—'
99
+ els.riskCount.textContent = '—'
100
+ setList(els.todos, [], () => '', 'run a scan to inspect source comments')
101
+ setList(els.risks, [], () => '', 'looks for env files, lockfiles, generated dirs, large source files')
102
+ setList(els.mix, [], () => '', 'file extension summary appears here')
103
+ })
@@ -0,0 +1,19 @@
1
+ # Static panel plugin template
2
+
3
+ A minimal CrewCode plugin built from plain HTML, CSS, and JavaScript.
4
+
5
+ ## What it demonstrates
6
+
7
+ - `crewcode.apiVersion: "0.1"` manifest compatibility.
8
+ - Sandboxed tab and sidebar panel entries.
9
+ - A command and status item that target the same panel.
10
+ - Read-only workspace capability calls through `crewcode-plugin-api.js`.
11
+
12
+ ## Install locally
13
+
14
+ ```bash
15
+ mkdir -p ~/.crewcode/plugins/static-panel-template
16
+ cp -R . ~/.crewcode/plugins/static-panel-template
17
+ ```
18
+
19
+ Then refresh the Plugins page, approve the plugin, and open `Static Panel`.
@@ -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,44 @@
1
+ {
2
+ "$schema": "../../../schemas/crewcode.plugin.schema.json",
3
+ "id": "static-panel-template",
4
+ "name": "Static Panel Template",
5
+ "version": "0.1.0",
6
+ "description": "Starter template for a sandboxed static HTML/CSS/JS plugin panel.",
7
+ "crewcode": { "apiVersion": "0.1" },
8
+ "permissions": ["workspace:read"],
9
+ "contributes": {
10
+ "tabs": [
11
+ {
12
+ "id": "main",
13
+ "title": "Static Panel",
14
+ "icon": "grid",
15
+ "entry": "panel.html",
16
+ "singleton": true
17
+ }
18
+ ],
19
+ "sidebarPanels": [
20
+ {
21
+ "id": "sidebar",
22
+ "title": "Static Panel",
23
+ "icon": "sidebar",
24
+ "entry": "panel.html"
25
+ }
26
+ ],
27
+ "commands": [
28
+ {
29
+ "id": "refresh",
30
+ "title": "Static Panel: Refresh Workspace Snapshot",
31
+ "icon": "refresh"
32
+ }
33
+ ],
34
+ "statusItems": [
35
+ {
36
+ "id": "status",
37
+ "title": "Static Panel Template",
38
+ "text": "plugin ready",
39
+ "icon": "plug",
40
+ "sidebarPanel": "sidebar"
41
+ }
42
+ ]
43
+ }
44
+ }
@@ -0,0 +1,36 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Static Panel Template</title>
6
+ <style>
7
+ :root { color-scheme: dark; }
8
+ body { margin: 0; background: #0f120f; color: #e6f0eb; font: 13px Inter, system-ui, sans-serif; }
9
+ main { padding: 16px; max-width: 760px; }
10
+ h1 { margin: 6px 0 8px; font-size: 22px; letter-spacing: -0.02em; }
11
+ p { color: #8b9a91; line-height: 1.5; }
12
+ button { background: #285a48; color: #e6f0eb; border: 1px solid #356b57; padding: 8px 10px; font: 11px 'JetBrains Mono', monospace; text-transform: lowercase; }
13
+ pre, code, .mono { font-family: 'JetBrains Mono', monospace; color: #9fbfaf; }
14
+ .eyebrow { color: #8b9a91; font-size: 11px; text-transform: lowercase; }
15
+ .panel { border: 1px solid #1c2f2f; background: #121712; padding: 12px; margin-top: 12px; }
16
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 8px; }
17
+ .metric strong { display: block; font-size: 24px; color: #e6f0eb; }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <main>
22
+ <div class="eyebrow mono">crewcode plugin template / static panel</div>
23
+ <h1>workspace snapshot</h1>
24
+ <p>This template uses static HTML, CSS, and JavaScript. It runs in a sandboxed iframe and talks to CrewCode only through <code>crewcode-plugin-api.js</code>.</p>
25
+ <button id="refresh">refresh files</button>
26
+ <section class="panel grid">
27
+ <div class="metric"><span class="mono">files</span><strong id="file-count">—</strong></div>
28
+ <div class="metric"><span class="mono">source files</span><strong id="source-count">—</strong></div>
29
+ <div class="metric"><span class="mono">docs</span><strong id="doc-count">—</strong></div>
30
+ </section>
31
+ <pre id="output" class="panel">waiting for context…</pre>
32
+ </main>
33
+ <script src="crewcode-plugin-api.js"></script>
34
+ <script src="plugin.js"></script>
35
+ </body>
36
+ </html>
@@ -0,0 +1,30 @@
1
+ const out = document.getElementById('output')
2
+ const fileCount = document.getElementById('file-count')
3
+ const sourceCount = document.getElementById('source-count')
4
+ const docCount = document.getElementById('doc-count')
5
+ const refresh = document.getElementById('refresh')
6
+
7
+ function renderContext(ctx) {
8
+ out.textContent = JSON.stringify({
9
+ source: ctx.openContext?.source,
10
+ workspace: ctx.workspace?.name,
11
+ root: ctx.workspace?.root,
12
+ }, null, 2)
13
+ }
14
+
15
+ async function loadSnapshot() {
16
+ try {
17
+ const result = await window.crewcode.workspace.listFiles()
18
+ const files = result.files || []
19
+ fileCount.textContent = String(files.length)
20
+ sourceCount.textContent = String(files.filter(file => /\.(ts|tsx|js|jsx|css|html)$/.test(file)).length)
21
+ docCount.textContent = String(files.filter(file => /\.(md|mdx|txt)$/.test(file)).length)
22
+ out.textContent = files.slice(0, 80).join('\n') || 'no files found'
23
+ } catch (err) {
24
+ out.textContent = `plugin request failed: ${err.message}`
25
+ }
26
+ }
27
+
28
+ window.crewcode.onContext(renderContext)
29
+ refresh.addEventListener('click', loadSnapshot)
30
+ void loadSnapshot()
@@ -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,13 @@
1
+ {
2
+ "$schema": "../../../schemas/crewcode.plugin.schema.json",
3
+ "id": "terminal-watchdog-lite",
4
+ "name": "Terminal Watchdog Lite",
5
+ "version": "0.1.0",
6
+ "description": "Dogfoods opt-in terminal watcher contribution without automatic terminal output access.",
7
+ "crewcode": { "apiVersion": "0.1" },
8
+ "permissions": ["terminal:read"],
9
+ "contributes": {
10
+ "sidebarPanels": [{ "id": "watchdog", "title": "Watchdog", "icon": "terminal", "entry": "panel.html" }],
11
+ "terminalWatchers": [{ "id": "watch-pane", "title": "Open Watchdog Lite", "icon": "terminal", "sidebarPanel": "watchdog", "mode": "opt-in" }]
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ <!doctype html><html><head><meta charset="utf-8"><title>Terminal Watchdog Lite</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>terminal watchdog lite</h1><p id="ctx">waiting for context…</p><div class="card">Opened from a terminal pane context menu. This validates explicit opt-in UX before terminal output is ever forwarded to plugins.</div><div class="card"><code>terminal:read</code> is approved, but v0 does not stream output yet.</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,40 @@
1
+ # TypeScript panel plugin template
2
+
3
+ This template shows how CrewCode plugins can be authored in TypeScript/React while still loading as isolated static panel assets.
4
+
5
+ ## Build
6
+
7
+ ```bash
8
+ npm install
9
+ npm run build
10
+ ```
11
+
12
+ The manifest points at `compiled/src/panel.html`. This checked-in compiled output lets the template run immediately after copying; rebuild it after editing `src/`.
13
+
14
+ ```json
15
+ {
16
+ "contributes": {
17
+ "tabs": [{ "entry": "compiled/src/panel.html" }]
18
+ }
19
+ }
20
+ ```
21
+
22
+ ## Install locally
23
+
24
+ Copy this folder into `~/.crewcode/plugins/typescript-panel-template`. It will run immediately from the checked-in `compiled/` assets. Build inside the copied folder after editing `src/`:
25
+
26
+ ```bash
27
+ mkdir -p ~/.crewcode/plugins/typescript-panel-template
28
+ cp -R . ~/.crewcode/plugins/typescript-panel-template
29
+ cd ~/.crewcode/plugins/typescript-panel-template
30
+ npm install
31
+ npm run build
32
+ ```
33
+
34
+ Restart CrewCode and open `TypeScript Plugin` from the command palette.
35
+
36
+ ## API
37
+
38
+ `src/crewcode-api.ts` is vendored from the official `packages/crewcode-plugin-api` source contract so copied templates work before the package is published. When consuming the package directly, import `crewcode` from `crewcode-plugin-api`.
39
+
40
+ Plugins are declared in the manifest, become globally active after enablement + approval, and can be disabled per workspace from CrewCode's Plugins page.