opencodekit 0.21.0 → 0.21.2
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/dist/index.js +4 -4
- package/dist/template/.opencode/AGENTS.md +51 -30
- package/dist/template/.opencode/agent/vision.md +0 -1
- package/dist/template/.opencode/memory.db +0 -0
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +2 -2
- package/dist/template/.opencode/package.json +20 -21
- package/dist/template/.opencode/plugin/README.md +0 -7
- package/dist/template/.opencode/plugin/copilot-auth.ts +59 -0
- package/dist/template/.opencode/plugin/prompt-leverage.ts +136 -138
- package/dist/template/.opencode/pnpm-lock.yaml +140 -706
- package/dist/template/.opencode/skill/agent-evals/SKILL.md +208 -0
- package/dist/template/.opencode/skill/anti-ai-slop/SKILL.md +76 -0
- package/dist/template/.opencode/skill/brand-asset-protocol/SKILL.md +222 -0
- package/dist/template/.opencode/skill/context-condensation/SKILL.md +149 -0
- package/dist/template/.opencode/skill/design-direction-advisor/SKILL.md +139 -0
- package/dist/template/.opencode/skill/hi-fi-prototype-html/SKILL.md +253 -0
- package/dist/template/.opencode/skill/html-deck-export/SKILL.md +189 -0
- package/dist/template/.opencode/skill/test-driven-development/SKILL.md +15 -0
- package/package.json +1 -1
- package/dist/template/.opencode/plugin/prompt-leverage.ts.bak +0 -228
- package/dist/template/.opencode/plugin/stitch.ts +0 -307
- package/dist/template/.opencode/skill/stitch/SKILL.md +0 -164
- package/dist/template/.opencode/skill/stitch-design-taste/DESIGN.md +0 -121
- package/dist/template/.opencode/skill/stitch-design-taste/SKILL.md +0 -197
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prompt Leverage Plugin
|
|
3
|
-
*
|
|
4
|
-
* Automatically upgrades every user prompt with the seven-block framework before the AI processes it.
|
|
5
|
-
* This enforces prompt-leverage on ALL user input without relying on the AI to remember the rule.
|
|
6
|
-
*
|
|
7
|
-
* Hooks used:
|
|
8
|
-
* - chat.message: Upgrades user message as it arrives
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
12
|
-
|
|
13
|
-
interface PromptFramework {
|
|
14
|
-
objective: string;
|
|
15
|
-
context: string;
|
|
16
|
-
workStyle: string;
|
|
17
|
-
toolRules: string;
|
|
18
|
-
outputContract: string;
|
|
19
|
-
verification: string;
|
|
20
|
-
doneCriteria: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const TASK_KEYWORDS: Record<string, string[]> = {
|
|
24
|
-
coding: [
|
|
25
|
-
"code",
|
|
26
|
-
"bug",
|
|
27
|
-
"repo",
|
|
28
|
-
"refactor",
|
|
29
|
-
"test",
|
|
30
|
-
"implement",
|
|
31
|
-
"fix",
|
|
32
|
-
"function",
|
|
33
|
-
"api",
|
|
34
|
-
"add",
|
|
35
|
-
"update",
|
|
36
|
-
"remove",
|
|
37
|
-
],
|
|
38
|
-
research: [
|
|
39
|
-
"research",
|
|
40
|
-
"compare",
|
|
41
|
-
"find",
|
|
42
|
-
"latest",
|
|
43
|
-
"sources",
|
|
44
|
-
"analyze",
|
|
45
|
-
"look up",
|
|
46
|
-
],
|
|
47
|
-
writing: [
|
|
48
|
-
"write",
|
|
49
|
-
"rewrite",
|
|
50
|
-
"draft",
|
|
51
|
-
"email",
|
|
52
|
-
"memo",
|
|
53
|
-
"blog",
|
|
54
|
-
"copy",
|
|
55
|
-
"tone",
|
|
56
|
-
],
|
|
57
|
-
review: ["review", "audit", "critique", "inspect", "evaluate", "assess"],
|
|
58
|
-
planning: ["plan", "roadmap", "strategy", "framework", "outline"],
|
|
59
|
-
analysis: ["analyze", "explain", "break down", "diagnose", "root cause"],
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
function detectTask(prompt: string): string {
|
|
63
|
-
const lowered = prompt.toLowerCase();
|
|
64
|
-
const scores: Record<string, number> = {};
|
|
65
|
-
|
|
66
|
-
for (const [task, keywords] of Object.entries(TASK_KEYWORDS)) {
|
|
67
|
-
scores[task] = keywords.filter((k) => lowered.includes(k)).length;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const best = Object.entries(scores).sort((a, b) => b[1] - a[1]);
|
|
71
|
-
return best[0]?.[1] ? best[0][0] : "analysis";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function inferIntensity(prompt: string, task: string): string {
|
|
75
|
-
const lowered = prompt.toLowerCase();
|
|
76
|
-
if (
|
|
77
|
-
lowered.includes("careful") ||
|
|
78
|
-
lowered.includes("deep") ||
|
|
79
|
-
lowered.includes("thorough") ||
|
|
80
|
-
lowered.includes("critical") ||
|
|
81
|
-
lowered.includes("production")
|
|
82
|
-
) {
|
|
83
|
-
return "Deep";
|
|
84
|
-
}
|
|
85
|
-
if (task === "coding" || task === "research" || task === "review") {
|
|
86
|
-
return "Standard";
|
|
87
|
-
}
|
|
88
|
-
return "Light";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function buildToolRules(task: string): string {
|
|
92
|
-
const rules: Record<string, string> = {
|
|
93
|
-
coding:
|
|
94
|
-
"Inspect the relevant files and dependencies first. Validate the final change with the narrowest useful checks before broadening scope.",
|
|
95
|
-
research:
|
|
96
|
-
"Retrieve evidence from reliable sources before concluding. Do not guess facts that can be checked.",
|
|
97
|
-
review:
|
|
98
|
-
"Read enough surrounding context to understand intent before critiquing. Distinguish confirmed issues from plausible risks.",
|
|
99
|
-
};
|
|
100
|
-
return (
|
|
101
|
-
rules[task] ||
|
|
102
|
-
"Use tools or extra context only when they materially improve correctness or completeness."
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function buildOutputContract(task: string): string {
|
|
107
|
-
const contracts: Record<string, string> = {
|
|
108
|
-
coding:
|
|
109
|
-
"Return the result in a practical execution format: concise summary, concrete changes or code, validation notes, and any remaining risks.",
|
|
110
|
-
research:
|
|
111
|
-
"Return a structured synthesis with key findings, supporting evidence, uncertainty where relevant, and a concise bottom line.",
|
|
112
|
-
writing:
|
|
113
|
-
"Return polished final copy in the requested tone and format. If useful, include a short rationale for major editorial choices.",
|
|
114
|
-
review:
|
|
115
|
-
"Return findings grouped by severity or importance, explain why each matters, and suggest the smallest credible next step.",
|
|
116
|
-
};
|
|
117
|
-
return (
|
|
118
|
-
contracts[task] ||
|
|
119
|
-
"Return a clear, well-structured response matched to the task, with no unnecessary verbosity."
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function upgradePrompt(userPrompt: string): string {
|
|
124
|
-
const normalized = userPrompt.trim().replace(/\s+/g, " ");
|
|
125
|
-
const task = detectTask(normalized);
|
|
126
|
-
const intensity = inferIntensity(normalized, task);
|
|
127
|
-
const toolRules = buildToolRules(task);
|
|
128
|
-
const outputContract = buildOutputContract(task);
|
|
129
|
-
|
|
130
|
-
// If prompt is already upgraded (contains framework blocks), skip
|
|
131
|
-
if (normalized.includes("Objective:") && normalized.includes("Context:")) {
|
|
132
|
-
return userPrompt;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Simple prompts don't need full framework
|
|
136
|
-
if (normalized.length < 15 || normalized.split(" ").length < 3) {
|
|
137
|
-
return userPrompt;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return `Objective:
|
|
141
|
-
- Complete this task: ${normalized}
|
|
142
|
-
- Optimize for a correct, useful result rather than a merely plausible one.
|
|
143
|
-
|
|
144
|
-
Context:
|
|
145
|
-
- Preserve the user's original intent and constraints.
|
|
146
|
-
- Surface any key assumptions if required information is missing.
|
|
147
|
-
|
|
148
|
-
Work Style:
|
|
149
|
-
- Task type: ${task}
|
|
150
|
-
- Effort level: ${intensity}
|
|
151
|
-
- Understand the problem broadly enough to avoid narrow mistakes, then go deep where the risk or complexity is highest.
|
|
152
|
-
- Use first-principles reasoning before proposing changes.
|
|
153
|
-
- For non-trivial work, review the result once with fresh eyes before finalizing.
|
|
154
|
-
|
|
155
|
-
Tool Rules:
|
|
156
|
-
- ${toolRules}
|
|
157
|
-
|
|
158
|
-
Output Contract:
|
|
159
|
-
- ${outputContract}
|
|
160
|
-
|
|
161
|
-
Verification:
|
|
162
|
-
- Check correctness, completeness, and edge cases.
|
|
163
|
-
- Improve obvious weaknesses if a better approach is available within scope.
|
|
164
|
-
|
|
165
|
-
Done Criteria:
|
|
166
|
-
- Stop only when the response satisfies the task, matches the requested format, and passes the verification step.`;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export const PromptLeverage: Plugin = async ({ client }) => {
|
|
170
|
-
console.log("PromptLeverage: Initializing...");
|
|
171
|
-
|
|
172
|
-
const showToast = async (message: string) => {
|
|
173
|
-
try {
|
|
174
|
-
await client.tui.showToast({
|
|
175
|
-
body: {
|
|
176
|
-
title: "PromptLeverage",
|
|
177
|
-
message,
|
|
178
|
-
variant: "info",
|
|
179
|
-
duration: 3000,
|
|
180
|
-
},
|
|
181
|
-
});
|
|
182
|
-
} catch {
|
|
183
|
-
/* Toast API unavailable */
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
"chat.message": async (input, output) => {
|
|
189
|
-
// Upgrade user message - check different structures
|
|
190
|
-
showToast(
|
|
191
|
-
`Hook fired, msg: ${JSON.stringify(input.message).slice(0, 100)}`,
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
// Try parts array
|
|
195
|
-
if (input.message?.parts) {
|
|
196
|
-
for (const part of input.message.parts) {
|
|
197
|
-
if (part.type === "text" && part.text) {
|
|
198
|
-
const upgraded = upgradePrompt(part.text);
|
|
199
|
-
if (upgraded !== part.text) {
|
|
200
|
-
part.text = upgraded;
|
|
201
|
-
showToast("Upgraded user prompt");
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// Try direct text field
|
|
207
|
-
else if (input.message?.text) {
|
|
208
|
-
const upgraded = upgradePrompt(input.message.text);
|
|
209
|
-
if (upgraded !== input.message.text) {
|
|
210
|
-
input.message.text = upgraded;
|
|
211
|
-
showToast("Upgraded user prompt");
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
// Try content array
|
|
215
|
-
else if (input.message?.content) {
|
|
216
|
-
for (const part of input.message.content) {
|
|
217
|
-
if (part.type === "text" && part.text) {
|
|
218
|
-
const upgraded = upgradePrompt(part.text);
|
|
219
|
-
if (upgraded !== part.text) {
|
|
220
|
-
part.text = upgraded;
|
|
221
|
-
showToast("Upgraded user prompt");
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
};
|
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stitch Plugin — Google Stitch UI generation as native OpenCode tools.
|
|
3
|
-
*
|
|
4
|
-
* Replaces the MCP subprocess (`npx @_davideast/stitch-mcp proxy`) with
|
|
5
|
-
* direct HTTP via `@google/stitch-sdk`'s `StitchToolClient`.
|
|
6
|
-
*
|
|
7
|
-
* Tools: stitch_create_project, stitch_get_project, stitch_list_projects,
|
|
8
|
-
* stitch_list_screens, stitch_get_screen, stitch_generate_screen,
|
|
9
|
-
* stitch_edit_screens, stitch_generate_variants
|
|
10
|
-
*
|
|
11
|
-
* Auth: Set STITCH_API_KEY env var (API key) or STITCH_ACCESS_TOKEN (OAuth).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { StitchError, StitchToolClient } from "@google/stitch-sdk";
|
|
15
|
-
import type { Plugin } from "@opencode-ai/plugin";
|
|
16
|
-
import { tool } from "@opencode-ai/plugin/tool";
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Shared
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
let client: StitchToolClient | null = null;
|
|
23
|
-
|
|
24
|
-
const getClient = (): StitchToolClient => {
|
|
25
|
-
if (!client) {
|
|
26
|
-
client = new StitchToolClient();
|
|
27
|
-
}
|
|
28
|
-
return client;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
/** Call a Stitch MCP tool and return the result as a JSON string. */
|
|
32
|
-
const callTool = async (
|
|
33
|
-
name: string,
|
|
34
|
-
args: Record<string, unknown>,
|
|
35
|
-
): Promise<string> => {
|
|
36
|
-
try {
|
|
37
|
-
const result = await getClient().callTool(name, args);
|
|
38
|
-
return JSON.stringify(result, null, 2);
|
|
39
|
-
} catch (err: unknown) {
|
|
40
|
-
if (err instanceof StitchError) {
|
|
41
|
-
return JSON.stringify({
|
|
42
|
-
error: err.code,
|
|
43
|
-
message: err.message,
|
|
44
|
-
...(err.suggestion ? { suggestion: err.suggestion } : {}),
|
|
45
|
-
recoverable: err.recoverable,
|
|
46
|
-
tool: name,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
return JSON.stringify({
|
|
50
|
-
error: "UNKNOWN_ERROR",
|
|
51
|
-
message: err instanceof Error ? err.message : String(err),
|
|
52
|
-
tool: name,
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Enums (for agent guidance — not strict validation)
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
const DEVICE_TYPES = [
|
|
62
|
-
"DEVICE_TYPE_UNSPECIFIED",
|
|
63
|
-
"MOBILE",
|
|
64
|
-
"DESKTOP",
|
|
65
|
-
"TABLET",
|
|
66
|
-
"AGNOSTIC",
|
|
67
|
-
] as const;
|
|
68
|
-
|
|
69
|
-
const MODEL_IDS = [
|
|
70
|
-
"MODEL_ID_UNSPECIFIED",
|
|
71
|
-
"GEMINI_3_PRO",
|
|
72
|
-
"GEMINI_3_FLASH",
|
|
73
|
-
] as const;
|
|
74
|
-
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// Plugin
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
|
|
79
|
-
export const StitchPlugin: Plugin = async () => {
|
|
80
|
-
return {
|
|
81
|
-
tool: {
|
|
82
|
-
// ---------------------------------------------------------------
|
|
83
|
-
// Projects
|
|
84
|
-
// ---------------------------------------------------------------
|
|
85
|
-
|
|
86
|
-
stitch_create_project: tool({
|
|
87
|
-
description:
|
|
88
|
-
"Create a new Google Stitch project.\n\nOptionally provide a title.",
|
|
89
|
-
args: {
|
|
90
|
-
title: tool.schema
|
|
91
|
-
.string()
|
|
92
|
-
.optional()
|
|
93
|
-
.describe("Project title (optional)"),
|
|
94
|
-
},
|
|
95
|
-
async execute(args: { title?: string }) {
|
|
96
|
-
return callTool("create_project", args);
|
|
97
|
-
},
|
|
98
|
-
}),
|
|
99
|
-
|
|
100
|
-
stitch_get_project: tool({
|
|
101
|
-
description:
|
|
102
|
-
"Get details of a Stitch project by resource name.\n\nFormat: projects/{projectId}",
|
|
103
|
-
args: {
|
|
104
|
-
name: tool.schema
|
|
105
|
-
.string()
|
|
106
|
-
.describe(
|
|
107
|
-
"Project resource name (e.g. projects/abc123). Required.",
|
|
108
|
-
),
|
|
109
|
-
},
|
|
110
|
-
async execute(args: { name: string }) {
|
|
111
|
-
return callTool("get_project", args);
|
|
112
|
-
},
|
|
113
|
-
}),
|
|
114
|
-
|
|
115
|
-
stitch_list_projects: tool({
|
|
116
|
-
description: "List all Stitch projects. Optionally filter by keyword.",
|
|
117
|
-
args: {
|
|
118
|
-
filter: tool.schema
|
|
119
|
-
.string()
|
|
120
|
-
.optional()
|
|
121
|
-
.describe("Filter string (optional)"),
|
|
122
|
-
},
|
|
123
|
-
async execute(args: { filter?: string }) {
|
|
124
|
-
return callTool("list_projects", args);
|
|
125
|
-
},
|
|
126
|
-
}),
|
|
127
|
-
|
|
128
|
-
// ---------------------------------------------------------------
|
|
129
|
-
// Screens
|
|
130
|
-
// ---------------------------------------------------------------
|
|
131
|
-
|
|
132
|
-
stitch_list_screens: tool({
|
|
133
|
-
description:
|
|
134
|
-
"List all screens in a Stitch project.\n\nRequires projectId.",
|
|
135
|
-
args: {
|
|
136
|
-
projectId: tool.schema.string().describe("Project ID. Required."),
|
|
137
|
-
},
|
|
138
|
-
async execute(args: { projectId: string }) {
|
|
139
|
-
return callTool("list_screens", args);
|
|
140
|
-
},
|
|
141
|
-
}),
|
|
142
|
-
|
|
143
|
-
stitch_get_screen: tool({
|
|
144
|
-
description:
|
|
145
|
-
"Get screen details including HTML code.\n\nProvide the screen resource name (format: projects/{projectId}/screens/{screenId}).",
|
|
146
|
-
args: {
|
|
147
|
-
name: tool.schema
|
|
148
|
-
.string()
|
|
149
|
-
.describe(
|
|
150
|
-
"Screen resource name (e.g. projects/abc/screens/xyz). Required.",
|
|
151
|
-
),
|
|
152
|
-
},
|
|
153
|
-
async execute(args: { name: string }) {
|
|
154
|
-
return callTool("get_screen", args);
|
|
155
|
-
},
|
|
156
|
-
}),
|
|
157
|
-
|
|
158
|
-
// ---------------------------------------------------------------
|
|
159
|
-
// Generation
|
|
160
|
-
// ---------------------------------------------------------------
|
|
161
|
-
|
|
162
|
-
stitch_generate_screen: tool({
|
|
163
|
-
description: `Generate a UI screen from a text prompt.
|
|
164
|
-
|
|
165
|
-
Device types: ${DEVICE_TYPES.join(", ")}
|
|
166
|
-
Model IDs: ${MODEL_IDS.join(", ")}`,
|
|
167
|
-
args: {
|
|
168
|
-
projectId: tool.schema.string().describe("Project ID. Required."),
|
|
169
|
-
prompt: tool.schema
|
|
170
|
-
.string()
|
|
171
|
-
.describe("Text description of the UI to generate. Required."),
|
|
172
|
-
deviceType: tool.schema
|
|
173
|
-
.string()
|
|
174
|
-
.optional()
|
|
175
|
-
.describe(
|
|
176
|
-
`Device type: ${DEVICE_TYPES.join(" | ")} (default: MOBILE)`,
|
|
177
|
-
),
|
|
178
|
-
modelId: tool.schema
|
|
179
|
-
.string()
|
|
180
|
-
.optional()
|
|
181
|
-
.describe(
|
|
182
|
-
`Model: ${MODEL_IDS.join(" | ")} (default: GEMINI_3_FLASH)`,
|
|
183
|
-
),
|
|
184
|
-
},
|
|
185
|
-
async execute(args: {
|
|
186
|
-
projectId: string;
|
|
187
|
-
prompt: string;
|
|
188
|
-
deviceType?: string;
|
|
189
|
-
modelId?: string;
|
|
190
|
-
}) {
|
|
191
|
-
return callTool("generate_screen_from_text", args);
|
|
192
|
-
},
|
|
193
|
-
}),
|
|
194
|
-
|
|
195
|
-
stitch_edit_screens: tool({
|
|
196
|
-
description: `Edit existing screens with a text prompt.
|
|
197
|
-
|
|
198
|
-
Device types: ${DEVICE_TYPES.join(", ")}
|
|
199
|
-
Model IDs: ${MODEL_IDS.join(", ")}`,
|
|
200
|
-
args: {
|
|
201
|
-
projectId: tool.schema.string().describe("Project ID. Required."),
|
|
202
|
-
selectedScreenIds: tool.schema
|
|
203
|
-
.array(tool.schema.string())
|
|
204
|
-
.describe("Screen IDs to edit. Required."),
|
|
205
|
-
prompt: tool.schema.string().describe("Edit instructions. Required."),
|
|
206
|
-
deviceType: tool.schema
|
|
207
|
-
.string()
|
|
208
|
-
.optional()
|
|
209
|
-
.describe(
|
|
210
|
-
`Device type: ${DEVICE_TYPES.join(" | ")} (default: MOBILE)`,
|
|
211
|
-
),
|
|
212
|
-
modelId: tool.schema
|
|
213
|
-
.string()
|
|
214
|
-
.optional()
|
|
215
|
-
.describe(
|
|
216
|
-
`Model: ${MODEL_IDS.join(" | ")} (default: GEMINI_3_FLASH)`,
|
|
217
|
-
),
|
|
218
|
-
},
|
|
219
|
-
async execute(args: {
|
|
220
|
-
projectId: string;
|
|
221
|
-
selectedScreenIds: string[];
|
|
222
|
-
prompt: string;
|
|
223
|
-
deviceType?: string;
|
|
224
|
-
modelId?: string;
|
|
225
|
-
}) {
|
|
226
|
-
return callTool("edit_screens", args);
|
|
227
|
-
},
|
|
228
|
-
}),
|
|
229
|
-
|
|
230
|
-
stitch_generate_variants: tool({
|
|
231
|
-
description: `Generate design variants of existing screens.
|
|
232
|
-
|
|
233
|
-
variantOptions:
|
|
234
|
-
- variantCount: number of variants (1-10)
|
|
235
|
-
- creativeRange: LOW, MEDIUM, HIGH (how different from original)
|
|
236
|
-
- aspects: optional comma-separated aspects to vary (e.g. "color,layout")
|
|
237
|
-
|
|
238
|
-
Device types: ${DEVICE_TYPES.join(", ")}
|
|
239
|
-
Model IDs: ${MODEL_IDS.join(", ")}`,
|
|
240
|
-
args: {
|
|
241
|
-
projectId: tool.schema.string().describe("Project ID. Required."),
|
|
242
|
-
selectedScreenIds: tool.schema
|
|
243
|
-
.array(tool.schema.string())
|
|
244
|
-
.describe("Screen IDs to create variants of. Required."),
|
|
245
|
-
prompt: tool.schema
|
|
246
|
-
.string()
|
|
247
|
-
.describe("Prompt describing desired variations. Required."),
|
|
248
|
-
variantCount: tool.schema
|
|
249
|
-
.number()
|
|
250
|
-
.optional()
|
|
251
|
-
.describe("Number of variants to generate (1-10, default: 3)"),
|
|
252
|
-
creativeRange: tool.schema
|
|
253
|
-
.string()
|
|
254
|
-
.optional()
|
|
255
|
-
.describe("Creative range: LOW | MEDIUM | HIGH (default: MEDIUM)"),
|
|
256
|
-
aspects: tool.schema
|
|
257
|
-
.string()
|
|
258
|
-
.optional()
|
|
259
|
-
.describe(
|
|
260
|
-
"Comma-separated aspects to vary (e.g. 'color,layout'). Optional.",
|
|
261
|
-
),
|
|
262
|
-
deviceType: tool.schema
|
|
263
|
-
.string()
|
|
264
|
-
.optional()
|
|
265
|
-
.describe(
|
|
266
|
-
`Device type: ${DEVICE_TYPES.join(" | ")} (default: MOBILE)`,
|
|
267
|
-
),
|
|
268
|
-
modelId: tool.schema
|
|
269
|
-
.string()
|
|
270
|
-
.optional()
|
|
271
|
-
.describe(
|
|
272
|
-
`Model: ${MODEL_IDS.join(" | ")} (default: GEMINI_3_FLASH)`,
|
|
273
|
-
),
|
|
274
|
-
},
|
|
275
|
-
async execute(args: {
|
|
276
|
-
projectId: string;
|
|
277
|
-
selectedScreenIds: string[];
|
|
278
|
-
prompt: string;
|
|
279
|
-
variantCount?: number;
|
|
280
|
-
creativeRange?: string;
|
|
281
|
-
aspects?: string;
|
|
282
|
-
deviceType?: string;
|
|
283
|
-
modelId?: string;
|
|
284
|
-
}) {
|
|
285
|
-
// Build variantOptions object from flat args
|
|
286
|
-
const variantOptions: Record<string, unknown> = {};
|
|
287
|
-
if (args.variantCount != null)
|
|
288
|
-
variantOptions.variantCount = args.variantCount;
|
|
289
|
-
if (args.creativeRange)
|
|
290
|
-
variantOptions.creativeRange = args.creativeRange;
|
|
291
|
-
if (args.aspects) variantOptions.aspects = args.aspects;
|
|
292
|
-
|
|
293
|
-
return callTool("generate_variants", {
|
|
294
|
-
projectId: args.projectId,
|
|
295
|
-
selectedScreenIds: args.selectedScreenIds,
|
|
296
|
-
prompt: args.prompt,
|
|
297
|
-
variantOptions,
|
|
298
|
-
...(args.deviceType ? { deviceType: args.deviceType } : {}),
|
|
299
|
-
...(args.modelId ? { modelId: args.modelId } : {}),
|
|
300
|
-
});
|
|
301
|
-
},
|
|
302
|
-
}),
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
export default StitchPlugin;
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: stitch
|
|
3
|
-
description: Use when generating, editing, or creating variants of UI screens in Google Stitch. MUST load before any stitch_generate_screen or stitch_edit_screens tool calls.
|
|
4
|
-
version: 2.0.0
|
|
5
|
-
tags: [design, ui, stitch]
|
|
6
|
-
dependencies: []
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Google Stitch Plugin
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
- When you need to generate or inspect Google Stitch UI designs.
|
|
14
|
-
|
|
15
|
-
## When NOT to Use
|
|
16
|
-
|
|
17
|
-
- When you don't have Stitch access or don't need Stitch-generated UI.
|
|
18
|
-
|
|
19
|
-
## Overview
|
|
20
|
-
|
|
21
|
-
Stitch tools are registered as native OpenCode tools via the Stitch plugin (`.opencode/plugin/stitch.ts`), using `@google/stitch-sdk` for direct HTTP to `stitch.googleapis.com/mcp`. No MCP subprocess needed.
|
|
22
|
-
|
|
23
|
-
## Prerequisites
|
|
24
|
-
|
|
25
|
-
1. **Google Cloud Project** with Stitch API enabled
|
|
26
|
-
2. **Google Cloud CLI** (`gcloud`) installed and initialized
|
|
27
|
-
3. **Required IAM Roles**:
|
|
28
|
-
- `roles/serviceusage.serviceUsageAdmin` (to enable the service)
|
|
29
|
-
- `roles/mcp.toolUser` (to call MCP tools)
|
|
30
|
-
|
|
31
|
-
## Setup Steps
|
|
32
|
-
|
|
33
|
-
### 1. Enable Stitch API in Google Cloud
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
gcloud config set project PROJECT_ID
|
|
37
|
-
gcloud beta services mcp enable stitch.googleapis.com --project=PROJECT_ID
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### 2. Set Environment Variables
|
|
41
|
-
|
|
42
|
-
**API Key auth** (recommended):
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
export STITCH_API_KEY="your-api-key"
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
**Or OAuth auth**:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
export STITCH_ACCESS_TOKEN=$(gcloud auth print-access-token)
|
|
52
|
-
export GOOGLE_CLOUD_PROJECT="your-project-id"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### 3. Restart OpenCode
|
|
56
|
-
|
|
57
|
-
Tools are available immediately after env vars are set and OpenCode restarts.
|
|
58
|
-
|
|
59
|
-
## Available Tools
|
|
60
|
-
|
|
61
|
-
| Tool | Description |
|
|
62
|
-
| -------------------------- | ------------------------------------ |
|
|
63
|
-
| `stitch_create_project` | Create a new Stitch project |
|
|
64
|
-
| `stitch_get_project` | Get project details by resource name |
|
|
65
|
-
| `stitch_list_projects` | List all projects (optional filter) |
|
|
66
|
-
| `stitch_list_screens` | List screens in a project |
|
|
67
|
-
| `stitch_get_screen` | Get screen details with HTML code |
|
|
68
|
-
| `stitch_generate_screen` | Generate UI from text prompt |
|
|
69
|
-
| `stitch_edit_screens` | Edit existing screens with a prompt |
|
|
70
|
-
| `stitch_generate_variants` | Generate design variants of screens |
|
|
71
|
-
|
|
72
|
-
## Usage Examples
|
|
73
|
-
|
|
74
|
-
### List Projects
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
stitch_list_projects({});
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Create a Project
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
stitch_create_project({ title: "My E-commerce App" });
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Generate Screen from Text
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
stitch_generate_screen({
|
|
90
|
-
projectId: "my-project-123",
|
|
91
|
-
prompt:
|
|
92
|
-
"Create a modern login page with email and password fields, social login buttons, and a forgot password link",
|
|
93
|
-
deviceType: "MOBILE",
|
|
94
|
-
});
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### Edit Existing Screens
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
stitch_edit_screens({
|
|
101
|
-
projectId: "my-project-123",
|
|
102
|
-
selectedScreenIds: ["screen-abc"],
|
|
103
|
-
prompt: "Make the login button larger and change the color scheme to dark mode",
|
|
104
|
-
});
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Generate Design Variants
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
stitch_generate_variants({
|
|
111
|
-
projectId: "my-project-123",
|
|
112
|
-
selectedScreenIds: ["screen-abc"],
|
|
113
|
-
prompt: "Create variants with different color schemes",
|
|
114
|
-
variantCount: 3,
|
|
115
|
-
creativeRange: "MEDIUM",
|
|
116
|
-
});
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Parameters
|
|
120
|
-
|
|
121
|
-
### Device Types
|
|
122
|
-
|
|
123
|
-
`DEVICE_TYPE_UNSPECIFIED` | `MOBILE` | `DESKTOP` | `TABLET` | `AGNOSTIC`
|
|
124
|
-
|
|
125
|
-
### Model IDs
|
|
126
|
-
|
|
127
|
-
`MODEL_ID_UNSPECIFIED` | `GEMINI_3_PRO` | `GEMINI_3_FLASH`
|
|
128
|
-
|
|
129
|
-
### Variant Options
|
|
130
|
-
|
|
131
|
-
- `variantCount`: Number of variants (1-10)
|
|
132
|
-
- `creativeRange`: `LOW` | `MEDIUM` | `HIGH`
|
|
133
|
-
- `aspects`: Comma-separated aspects to vary (e.g. "color,layout")
|
|
134
|
-
|
|
135
|
-
## Troubleshooting
|
|
136
|
-
|
|
137
|
-
### "AUTH_FAILED"
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
# API key auth
|
|
141
|
-
export STITCH_API_KEY="your-key"
|
|
142
|
-
|
|
143
|
-
# Or OAuth (token expires after ~1 hour)
|
|
144
|
-
export STITCH_ACCESS_TOKEN=$(gcloud auth print-access-token)
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### "Stitch API not enabled"
|
|
148
|
-
|
|
149
|
-
```bash
|
|
150
|
-
gcloud beta services mcp enable stitch.googleapis.com --project=YOUR_PROJECT_ID
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
## Documentation
|
|
154
|
-
|
|
155
|
-
- [Google Stitch](https://stitch.withgoogle.com)
|
|
156
|
-
- [Stitch SDK](https://github.com/google-labs-code/stitch-sdk)
|
|
157
|
-
- [Stitch MCP Setup](https://stitch.withgoogle.com/docs/mcp/setup)
|
|
158
|
-
|
|
159
|
-
## Tips
|
|
160
|
-
|
|
161
|
-
- API key auth is simpler than OAuth (no token refresh)
|
|
162
|
-
- Use descriptive prompts for better UI generation
|
|
163
|
-
- `GEMINI_3_PRO` produces higher quality; `GEMINI_3_FLASH` is faster
|
|
164
|
-
- Test generated code in your target framework before production use
|