@dcl-regenesislabs/opendcl 0.2.1-26165320302.commit-e6effe4 → 0.2.1-26502482653.commit-5089b10

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 (45) hide show
  1. package/README.md +5 -3
  2. package/context/sdk7-cheat-sheet.md +4 -0
  3. package/dist/index.js +0 -12
  4. package/dist/index.js.map +1 -1
  5. package/extensions/dcl-init.ts +58 -6
  6. package/package.json +3 -3
  7. package/prompts/system.md +71 -41
  8. package/skills/add-3d-models/SKILL.md +120 -70
  9. package/skills/add-interactivity/SKILL.md +74 -2
  10. package/skills/advanced-input/SKILL.md +34 -1
  11. package/skills/advanced-rendering/SKILL.md +82 -9
  12. package/skills/animations-tweens/SKILL.md +203 -98
  13. package/skills/audio-analysis/SKILL.md +164 -0
  14. package/skills/audio-video/SKILL.md +184 -83
  15. package/skills/build-ui/SKILL.md +25 -2
  16. package/skills/camera-control/SKILL.md +78 -7
  17. package/skills/create-scene/SKILL.md +56 -13
  18. package/skills/deploy-scene/SKILL.md +12 -0
  19. package/skills/deploy-worlds/SKILL.md +35 -0
  20. package/skills/editor-gizmo/.gitignore +11 -0
  21. package/skills/editor-gizmo/SKILL.md +222 -0
  22. package/skills/editor-gizmo/src/__editor/camera.ts +277 -0
  23. package/skills/editor-gizmo/src/__editor/discovery.ts +210 -0
  24. package/skills/editor-gizmo/src/__editor/drag.ts +265 -0
  25. package/skills/editor-gizmo/src/__editor/gizmo.ts +496 -0
  26. package/skills/editor-gizmo/src/__editor/history.ts +72 -0
  27. package/skills/editor-gizmo/src/__editor/index.ts +138 -0
  28. package/skills/editor-gizmo/src/__editor/input.ts +55 -0
  29. package/skills/editor-gizmo/src/__editor/math-utils.ts +114 -0
  30. package/skills/editor-gizmo/src/__editor/persistence.ts +113 -0
  31. package/skills/editor-gizmo/src/__editor/selection.ts +157 -0
  32. package/skills/editor-gizmo/src/__editor/state.ts +117 -0
  33. package/skills/editor-gizmo/src/__editor/ui.tsx +699 -0
  34. package/skills/game-design/SKILL.md +1 -2
  35. package/skills/lighting-environment/SKILL.md +103 -56
  36. package/skills/multiplayer-sync/SKILL.md +31 -2
  37. package/skills/nft-blockchain/SKILL.md +45 -40
  38. package/skills/npcs/SKILL.md +180 -0
  39. package/skills/optimize-scene/SKILL.md +7 -2
  40. package/skills/particle-system/SKILL.md +222 -0
  41. package/skills/player-avatar/SKILL.md +133 -7
  42. package/skills/player-physics/SKILL.md +93 -0
  43. package/skills/scene-runtime/SKILL.md +9 -5
  44. package/skills/visual-feedback/SKILL.md +1 -0
  45. package/extensions/dcl-setup-ollama.ts +0 -312
@@ -1,312 +0,0 @@
1
- /**
2
- * DCL Setup Ollama Extension
3
- *
4
- * Registers the /setup-ollama command that walks users through
5
- * model selection and configuration for a free local LLM setup.
6
- * On session start, nudges users who have no provider configured.
7
- */
8
-
9
- import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
10
- import { readFile, writeFile, mkdir } from "node:fs/promises";
11
- import { dirname, join } from "node:path";
12
- import { homedir } from "node:os";
13
- import { spawn } from "node:child_process";
14
-
15
- interface OllamaProvider {
16
- baseUrl?: string;
17
- api?: string;
18
- apiKey?: string;
19
- models?: { id: string }[];
20
- }
21
-
22
- const API_KEY_ENV_VARS = [
23
- "ANTHROPIC_API_KEY",
24
- "OPENAI_API_KEY",
25
- "GOOGLE_API_KEY",
26
- "OPENROUTER_API_KEY",
27
- ];
28
-
29
- export const OLLAMA_MODELS = [
30
- { id: "qwen2.5-coder:32b", label: "qwen2.5-coder:32b (Recommended — best coding benchmarks, ~18GB)" },
31
- { id: "qwen3-coder:30b", label: "qwen3-coder:30b (Latest Alibaba coder, 256K context, ~19GB)" },
32
- { id: "devstral:24b", label: "devstral:24b (Mistral coding agent model, ~14GB)" },
33
- { id: "glm-4.7-flash", label: "glm-4.7-flash (Reasoning + code generation, ~25GB)" },
34
- ];
35
-
36
- async function readJsonFile<T>(path: string, fallback: T): Promise<T> {
37
- try {
38
- const content = await readFile(path, "utf-8");
39
- return JSON.parse(content);
40
- } catch {
41
- return fallback;
42
- }
43
- }
44
-
45
- async function writeJsonFile(path: string, data: unknown): Promise<void> {
46
- await mkdir(dirname(path), { recursive: true });
47
- await writeFile(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
48
- }
49
-
50
- export async function writeModelsConfig(modelsPath: string, modelId: string): Promise<void> {
51
- const config = await readJsonFile<{ providers?: Record<string, unknown> }>(modelsPath, {});
52
- config.providers ??= {};
53
-
54
- const ollama = config.providers.ollama as OllamaProvider | undefined;
55
- if (ollama) {
56
- ollama.models ??= [];
57
- if (!ollama.models.some((m) => m.id === modelId)) {
58
- ollama.models.push({ id: modelId });
59
- }
60
- } else {
61
- config.providers.ollama = {
62
- baseUrl: "http://localhost:11434/v1",
63
- api: "openai-completions",
64
- apiKey: "ollama",
65
- models: [{ id: modelId }],
66
- };
67
- }
68
-
69
- await writeJsonFile(modelsPath, config);
70
- }
71
-
72
- export async function setDefaultModel(settingsPath: string, provider: string, modelId: string): Promise<void> {
73
- const settings = await readJsonFile<Record<string, unknown>>(settingsPath, {});
74
-
75
- settings.defaultProvider = provider;
76
- settings.defaultModel = modelId;
77
-
78
- await writeJsonFile(settingsPath, settings);
79
- }
80
-
81
- export async function isProviderConfigured(): Promise<boolean> {
82
- if (API_KEY_ENV_VARS.some((v) => process.env[v])) {
83
- return true;
84
- }
85
-
86
- const configDir = join(homedir(), ".opendcl", "agent");
87
-
88
- const modelsConfig = await readJsonFile<{ providers?: Record<string, unknown> }>(join(configDir, "models.json"), {});
89
- if (modelsConfig.providers != null && Object.keys(modelsConfig.providers).length > 0) {
90
- return true;
91
- }
92
-
93
- const authConfig = await readJsonFile<Record<string, unknown>>(join(configDir, "auth.json"), {});
94
- return Object.keys(authConfig).length > 0;
95
- }
96
-
97
- export function parseOllamaList(output: string): string[] {
98
- const lines = output.split(/\r?\n/).filter((l) => l.trim());
99
- // Skip header line (starts with "NAME")
100
- const dataLines = lines.filter((l) => !l.startsWith("NAME"));
101
- return dataLines
102
- .map((l) => l.split(/\s+/)[0])
103
- .filter(Boolean)
104
- .map((name) => name.replace(/:latest$/, ""));
105
- }
106
-
107
- export async function removeOllamaModel(
108
- modelsPath: string,
109
- settingsPath: string,
110
- modelId: string,
111
- ): Promise<void> {
112
- const config = await readJsonFile<{ providers?: Record<string, unknown> }>(modelsPath, {});
113
- const ollama = config.providers?.ollama as OllamaProvider | undefined;
114
- if (ollama?.models) {
115
- ollama.models = ollama.models.filter((m) => m.id !== modelId);
116
- if (ollama.models.length === 0) {
117
- delete config.providers!.ollama;
118
- }
119
- await writeJsonFile(modelsPath, config);
120
- }
121
-
122
- const settings = await readJsonFile<Record<string, unknown>>(settingsPath, {});
123
- if (settings.defaultProvider === "ollama" && settings.defaultModel === modelId) {
124
- delete settings.defaultProvider;
125
- delete settings.defaultModel;
126
- await writeJsonFile(settingsPath, settings);
127
- }
128
- }
129
-
130
- /**
131
- * Run ollama commands through the user's login shell so the full PATH is used.
132
- * (pi.exec without shell won't find binaries added by installers to profile files.)
133
- */
134
- function ollamaExec(pi: Parameters<ExtensionFactory>[0], args: string, timeout = 10000) {
135
- const shell = process.env.SHELL || "/bin/sh";
136
- return pi.exec(shell, ["-lc", `ollama ${args}`], { timeout }).catch(() => null);
137
- }
138
-
139
- function stripAnsi(text: string): string {
140
- return text.replace(/\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g, "");
141
- }
142
-
143
- /**
144
- * Extract a clean, human-readable progress line from raw ollama pull output.
145
- * Ollama outputs ANSI-heavy terminal UI — this strips escape codes and
146
- * picks the most informative line (percentage progress > phase labels).
147
- */
148
- export function extractPullProgress(raw: string): string | null {
149
- const clean = stripAnsi(raw);
150
- const lines = clean.split(/\r\n|\r|\n/).map((l) => l.trim()).filter(Boolean);
151
- // Prefer the line with download percentage (e.g. "pulling abc123: 45% 8 GB/18 GB 12 MB/s")
152
- const progress = lines.findLast((l) => /\d+%/.test(l));
153
- if (progress) return progress;
154
- // Fall back to phase lines like "pulling manifest", "verifying sha256 digest"
155
- const phase = lines.findLast((l) => /^(pulling|verifying|writing|success)/.test(l));
156
- return phase ?? null;
157
- }
158
-
159
- /**
160
- * Pull a model with streaming progress via spawn.
161
- * Throttles notifications to avoid UI spam (one update every 2 seconds max).
162
- */
163
- export function ollamaPull(
164
- modelId: string,
165
- onProgress: (line: string) => void,
166
- ): Promise<{ success: boolean; error?: string }> {
167
- return new Promise((resolve) => {
168
- const shell = process.env.SHELL || "/bin/sh";
169
- const child = spawn(shell, ["-lc", `ollama pull ${modelId}`], {
170
- stdio: "pipe",
171
- });
172
-
173
- let lastNotify = 0;
174
- let lastLine = "";
175
- let errorOutput = "";
176
-
177
- function handleData(data: Buffer): void {
178
- const text = data.toString();
179
- errorOutput += text;
180
- const line = extractPullProgress(text);
181
- if (!line) return;
182
- lastLine = line;
183
- const now = Date.now();
184
- if (now - lastNotify >= 2000) {
185
- lastNotify = now;
186
- onProgress(line);
187
- }
188
- }
189
-
190
- child.stdout?.on("data", handleData);
191
- child.stderr?.on("data", handleData);
192
-
193
- child.on("error", (err) => {
194
- resolve({ success: false, error: err.message });
195
- });
196
-
197
- child.on("exit", (code) => {
198
- if (lastLine) onProgress(lastLine);
199
- if (code === 0) {
200
- resolve({ success: true });
201
- } else {
202
- resolve({ success: false, error: stripAnsi(errorOutput).slice(-500) });
203
- }
204
- });
205
- });
206
- }
207
-
208
- const extension: ExtensionFactory = (pi) => {
209
- pi.on("session_start", async (_event, ctx) => {
210
- if (!(await isProviderConfigured())) {
211
- ctx.ui.notify(
212
- "Get started by running /setup to configure a cloud provider",
213
- "warning",
214
- );
215
- return;
216
- }
217
-
218
- const configDir = join(homedir(), ".opendcl", "agent");
219
- const settingsPath = join(configDir, "settings.json");
220
- const settings = await readJsonFile<Record<string, unknown>>(settingsPath, {});
221
- if (settings.defaultProvider !== "ollama") return;
222
-
223
- const defaultModel = settings.defaultModel as string | undefined;
224
- if (!defaultModel) return;
225
-
226
- const listResult = await ollamaExec(pi, "list");
227
- if (!listResult || listResult.code !== 0) return;
228
-
229
- const installed = parseOllamaList(listResult.stdout || "");
230
- if (installed.includes(defaultModel)) return;
231
-
232
- const modelsPath = join(configDir, "models.json");
233
- await removeOllamaModel(modelsPath, settingsPath, defaultModel);
234
- ctx.ui.notify(
235
- `Model '${defaultModel}' is no longer installed in Ollama. Run /setup-ollama to configure a new model.`,
236
- "warning",
237
- );
238
- });
239
-
240
- pi.registerCommand("setup-ollama", {
241
- description: "Configure Ollama as your free local LLM provider",
242
- handler: async (_args, ctx) => {
243
- const configDir = join(homedir(), ".opendcl", "agent");
244
-
245
- const versionResult = await ollamaExec(pi, "--version");
246
- if (!versionResult || versionResult.code !== 0) {
247
- ctx.ui.notify("Ollama is not installed. Download it from https://ollama.com then run /setup-ollama again.", "warning");
248
- return;
249
- }
250
-
251
- const listResult = await ollamaExec(pi, "list");
252
- if (!listResult || listResult.code !== 0) {
253
- ctx.ui.notify("Ollama is installed but not running. Start it with 'ollama serve', then run /setup-ollama again.", "warning");
254
- return;
255
- }
256
-
257
- const installed = parseOllamaList(listResult.stdout || "");
258
- const theme = ctx.ui.theme;
259
- const sorted = [...OLLAMA_MODELS].sort((a, b) => {
260
- const aInstalled = installed.includes(a.id) ? 0 : 1;
261
- const bInstalled = installed.includes(b.id) ? 0 : 1;
262
- return aInstalled - bInstalled;
263
- });
264
- const labels = sorted.map((m) =>
265
- installed.includes(m.id)
266
- ? `${m.label} ${theme.fg("success", "● ready")}`
267
- : `${m.label} ${theme.fg("dim", "○ needs download")}`,
268
- );
269
-
270
- const selected = await ctx.ui.select("Which model do you want to use?", labels);
271
- if (!selected) {
272
- ctx.ui.notify("Setup cancelled.", "info");
273
- return;
274
- }
275
-
276
- const model = sorted.find((m) => selected.startsWith(m.label));
277
- if (!model) {
278
- ctx.ui.notify("Invalid selection.", "error");
279
- return;
280
- }
281
-
282
- const alreadyInstalled = installed.includes(model.id);
283
- if (!alreadyInstalled) {
284
- ctx.ui.setStatus("pull", `Pulling ${model.id}...`);
285
- const pullResult = await ollamaPull(model.id, (line) => {
286
- ctx.ui.setStatus("pull", line);
287
- });
288
- ctx.ui.setStatus("pull", undefined);
289
- if (!pullResult.success) {
290
- ctx.ui.notify(`Failed to pull model: ${pullResult.error || "unknown error"}`, "error");
291
- return;
292
- }
293
- }
294
- ctx.ui.notify("Model ready.", "info");
295
-
296
- const modelsPath = join(configDir, "models.json");
297
- await writeModelsConfig(modelsPath, model.id);
298
-
299
- const settingsPath = join(configDir, "settings.json");
300
- await setDefaultModel(settingsPath, "ollama", model.id);
301
-
302
- ctx.ui.notify(
303
- `Ollama configured: ${model.id}. Reloading...`,
304
- "info",
305
- );
306
- await ctx.reload();
307
- return;
308
- },
309
- });
310
- };
311
-
312
- export default extension;