groove-dev 0.27.131 → 0.27.134
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/AGENT_ORCHESTRATION.md +375 -0
- package/moe-training/shared/envelope-schema.js +1 -1
- package/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/index.js +3 -1
- package/node_modules/@groove-dev/daemon/src/introducer.js +48 -4
- package/node_modules/@groove-dev/daemon/src/llama-server.js +4 -4
- package/node_modules/@groove-dev/daemon/src/model-lab.js +8 -0
- package/node_modules/@groove-dev/daemon/src/preview.js +85 -58
- package/node_modules/@groove-dev/daemon/src/process.js +9 -0
- package/node_modules/@groove-dev/daemon/src/terminal-pty.js +24 -14
- package/node_modules/@groove-dev/daemon/src/validate.js +0 -4
- package/{packages/gui/dist/assets/codemirror-CFF1Lrnz.js → node_modules/@groove-dev/gui/dist/assets/codemirror-DRQdprYi.js} +11 -11
- package/node_modules/@groove-dev/gui/dist/assets/index-BgQL4bNl.css +1 -0
- package/{packages/gui/dist/assets/index-BiB9oY9U.js → node_modules/@groove-dev/gui/dist/assets/index-Dozp69tK.js} +1721 -1721
- package/node_modules/@groove-dev/gui/dist/index.html +3 -3
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/app.css +6 -6
- package/node_modules/@groove-dev/gui/src/components/agents/agent-chat.jsx +12 -1
- package/node_modules/@groove-dev/gui/src/components/agents/agent-feed.jsx +15 -5
- package/node_modules/@groove-dev/gui/src/components/agents/agent-file-tree.jsx +6 -6
- package/node_modules/@groove-dev/gui/src/components/agents/workspace-mode.jsx +11 -9
- package/node_modules/@groove-dev/gui/src/components/editor/code-editor.jsx +26 -3
- package/node_modules/@groove-dev/gui/src/components/editor/file-tree.jsx +6 -6
- package/node_modules/@groove-dev/gui/src/components/editor/terminal.jsx +20 -8
- package/node_modules/@groove-dev/gui/src/components/lab/chat-playground.jsx +10 -1
- package/node_modules/@groove-dev/gui/src/components/lab/lab-assistant.jsx +4 -4
- package/node_modules/@groove-dev/gui/src/components/lab/system-prompt-editor.jsx +17 -3
- package/node_modules/@groove-dev/gui/src/components/layout/terminal-panel.jsx +2 -4
- package/node_modules/@groove-dev/gui/src/components/preview/preview-toolbar.jsx +8 -6
- package/node_modules/@groove-dev/gui/src/stores/groove.js +82 -15
- package/node_modules/@groove-dev/gui/src/views/agents.jsx +82 -74
- package/node_modules/@groove-dev/gui/src/views/editor.jsx +11 -9
- package/node_modules/moe-training/shared/envelope-schema.js +1 -1
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/index.js +3 -1
- package/packages/daemon/src/introducer.js +48 -4
- package/packages/daemon/src/llama-server.js +4 -4
- package/packages/daemon/src/model-lab.js +8 -0
- package/packages/daemon/src/preview.js +85 -58
- package/packages/daemon/src/process.js +9 -0
- package/packages/daemon/src/terminal-pty.js +24 -14
- package/packages/daemon/src/validate.js +0 -4
- package/{node_modules/@groove-dev/gui/dist/assets/codemirror-CFF1Lrnz.js → packages/gui/dist/assets/codemirror-DRQdprYi.js} +11 -11
- package/packages/gui/dist/assets/index-BgQL4bNl.css +1 -0
- package/{node_modules/@groove-dev/gui/dist/assets/index-BiB9oY9U.js → packages/gui/dist/assets/index-Dozp69tK.js} +1721 -1721
- package/packages/gui/dist/index.html +3 -3
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/app.css +6 -6
- package/packages/gui/src/components/agents/agent-chat.jsx +12 -1
- package/packages/gui/src/components/agents/agent-feed.jsx +15 -5
- package/packages/gui/src/components/agents/agent-file-tree.jsx +6 -6
- package/packages/gui/src/components/agents/workspace-mode.jsx +11 -9
- package/packages/gui/src/components/editor/code-editor.jsx +26 -3
- package/packages/gui/src/components/editor/file-tree.jsx +6 -6
- package/packages/gui/src/components/editor/terminal.jsx +20 -8
- package/packages/gui/src/components/lab/chat-playground.jsx +10 -1
- package/packages/gui/src/components/lab/lab-assistant.jsx +4 -4
- package/packages/gui/src/components/lab/system-prompt-editor.jsx +17 -3
- package/packages/gui/src/components/layout/terminal-panel.jsx +2 -4
- package/packages/gui/src/components/preview/preview-toolbar.jsx +8 -6
- package/packages/gui/src/stores/groove.js +82 -15
- package/packages/gui/src/views/agents.jsx +82 -74
- package/packages/gui/src/views/editor.jsx +11 -9
- package/CENTRAL_COMMAND_REBUILD.md +0 -689
- package/MERKLE_TREE_ARCHITECTURE.md +0 -354
- package/node_modules/@groove-dev/gui/dist/assets/index-CeyDFVub.css +0 -1
- package/packages/gui/dist/assets/index-CeyDFVub.css +0 -1
|
@@ -116,67 +116,94 @@ export class PreviewService {
|
|
|
116
116
|
return { launched: false, reason: installResult.reason };
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
119
|
+
// Static-first strategy: always try build+static before dev-server.
|
|
120
|
+
// Static serving is near-100% reliable — no port conflicts, no proxy
|
|
121
|
+
// issues, no HMR quirks, no URL rewriting. Dev servers fail constantly
|
|
122
|
+
// in AI-generated projects (wrong ports, missing scripts, relative path
|
|
123
|
+
// breakage through the proxy). Only fall back to dev-server when there
|
|
124
|
+
// is no build output at all.
|
|
125
|
+
|
|
126
|
+
const openFile = (preview.openPath || 'index.html').replace(/^\/+/, '');
|
|
127
|
+
const pkgPath = resolve(baseDir, 'package.json');
|
|
128
|
+
let hasBuildScript = false;
|
|
129
|
+
let isFrontendProject = false;
|
|
130
|
+
try {
|
|
131
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
132
|
+
hasBuildScript = !!pkg.scripts?.build;
|
|
133
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
134
|
+
isFrontendProject = !!(allDeps?.react || allDeps?.vue || allDeps?.svelte || allDeps?.vite
|
|
135
|
+
|| allDeps?.['@angular/core'] || allDeps?.next || allDeps?.nuxt || allDeps?.astro);
|
|
136
|
+
} catch { /* no package.json or malformed */ }
|
|
137
|
+
|
|
138
|
+
// Also detect frontend by config files
|
|
139
|
+
if (!isFrontendProject) {
|
|
140
|
+
const frontendConfigs = ['vite.config', 'next.config', 'webpack.config', 'svelte.config', 'astro.config', 'nuxt.config', 'angular.json'];
|
|
141
|
+
for (const cfg of frontendConfigs) {
|
|
142
|
+
if (['.js', '.ts', '.mjs', '.cjs', ''].some((ext) => existsSync(resolve(baseDir, cfg + ext)))) {
|
|
143
|
+
isFrontendProject = true;
|
|
144
|
+
break;
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Common build output directories
|
|
150
|
+
const OUTPUT_DIRS = ['dist', 'build', 'out', '.next/standalone', 'public'];
|
|
151
|
+
|
|
152
|
+
let result;
|
|
153
|
+
|
|
154
|
+
// Strategy 1: Build and serve static (only for frontend projects — skip for backends)
|
|
155
|
+
if (hasBuildScript && (isFrontendProject || preview.kind === 'static-html')) {
|
|
156
|
+
const buildResult = this._runBuild(teamId, baseDir);
|
|
157
|
+
if (!buildResult?.failed) {
|
|
158
|
+
result = this._findAndServeStatic(teamId, baseDir, OUTPUT_DIRS, openFile, preview);
|
|
159
|
+
if (result && (await result).launched) {
|
|
160
|
+
result = await result;
|
|
161
|
+
this.daemon.audit?.log('preview.launched', { teamId, url: result.url, kind: result.kind, baseDir, strategy: 'build-static' });
|
|
162
|
+
return result;
|
|
147
163
|
}
|
|
148
164
|
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Strategy 2: Serve existing static files (pre-built, plain HTML, no-build projects)
|
|
168
|
+
result = this._findAndServeStatic(teamId, baseDir, OUTPUT_DIRS, openFile, preview);
|
|
169
|
+
if (result && (await result).launched) {
|
|
170
|
+
result = await result;
|
|
171
|
+
this.daemon.audit?.log('preview.launched', { teamId, url: result.url, kind: result.kind, baseDir, strategy: 'serve-existing' });
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Strategy 3: Dev server (backends, SSR frameworks, projects with no static output)
|
|
176
|
+
if (preview.kind === 'dev-server') {
|
|
149
177
|
result = await this._launchDevServer(teamId, baseDir, preview);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (existsSync(pkgPath)) {
|
|
154
|
-
try {
|
|
155
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
156
|
-
if (pkg.scripts?.build) {
|
|
157
|
-
const buildResult = this._runBuild(teamId, baseDir);
|
|
158
|
-
if (!buildResult?.failed) {
|
|
159
|
-
const distDir = resolve(baseDir, 'dist');
|
|
160
|
-
if (existsSync(resolve(distDir, 'index.html'))) {
|
|
161
|
-
result = await this._launchStatic(teamId, distDir, { ...preview, openPath: 'index.html' });
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
} catch { /* fallback failed, keep original error */ }
|
|
166
|
-
}
|
|
178
|
+
if (result?.launched) {
|
|
179
|
+
this.daemon.audit?.log('preview.launched', { teamId, url: result.url, kind: result.kind, baseDir, strategy: 'dev-server' });
|
|
180
|
+
return result;
|
|
167
181
|
}
|
|
168
|
-
} else {
|
|
169
|
-
result = { launched: false, reason: `unknown_kind: ${preview.kind}` };
|
|
170
182
|
}
|
|
171
183
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
} else {
|
|
175
|
-
this.daemon.audit?.log('preview.failed', { teamId, reason: result.reason, baseDir });
|
|
176
|
-
}
|
|
184
|
+
result = result || { launched: false, reason: 'all_strategies_failed' };
|
|
185
|
+
this.daemon.audit?.log('preview.failed', { teamId, reason: result.reason, baseDir });
|
|
177
186
|
return result;
|
|
178
187
|
}
|
|
179
188
|
|
|
189
|
+
_findAndServeStatic(teamId, baseDir, outputDirs, openFile, preview) {
|
|
190
|
+
// Check output directories first, then baseDir itself
|
|
191
|
+
for (const dir of outputDirs) {
|
|
192
|
+
const fullDir = resolve(baseDir, dir);
|
|
193
|
+
if (existsSync(resolve(fullDir, openFile))) {
|
|
194
|
+
return this._launchStatic(teamId, fullDir, { ...preview, openPath: openFile });
|
|
195
|
+
}
|
|
196
|
+
if (openFile !== 'index.html' && existsSync(resolve(fullDir, 'index.html'))) {
|
|
197
|
+
return this._launchStatic(teamId, fullDir, { ...preview, openPath: 'index.html' });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// Finally check baseDir itself (plain HTML projects)
|
|
201
|
+
if (existsSync(resolve(baseDir, openFile))) {
|
|
202
|
+
return this._launchStatic(teamId, baseDir, { ...preview, openPath: openFile });
|
|
203
|
+
}
|
|
204
|
+
return Promise.resolve({ launched: false, reason: 'no_static_entry' });
|
|
205
|
+
}
|
|
206
|
+
|
|
180
207
|
_ensureDependencies(teamId, baseDir) {
|
|
181
208
|
const pkgPath = resolve(baseDir, 'package.json');
|
|
182
209
|
const nodeModules = resolve(baseDir, 'node_modules');
|
|
@@ -262,18 +289,18 @@ export class PreviewService {
|
|
|
262
289
|
if (!pkg.scripts?.build) return { failed: true, reason: 'no build script' };
|
|
263
290
|
} catch { return { failed: true, reason: 'malformed package.json' }; }
|
|
264
291
|
|
|
265
|
-
const isVite = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs']
|
|
292
|
+
const isVite = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs']
|
|
266
293
|
.some((f) => existsSync(resolve(baseDir, f)));
|
|
267
294
|
|
|
268
295
|
let command = 'npm run build';
|
|
269
|
-
const buildScript = (pkg.scripts.build || '').trim();
|
|
270
|
-
|
|
271
|
-
if (isVite && /^(tsc\s*&&\s*)?vite\s+build\s*$/.test(buildScript)) {
|
|
272
|
-
command = `npm run build -- --base=./`;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
296
|
const env = { ...process.env };
|
|
276
|
-
|
|
297
|
+
|
|
298
|
+
// Force relative base paths for Vite — absolute paths break in iframe
|
|
299
|
+
if (isVite) {
|
|
300
|
+
const buildScript = (pkg.scripts.build || '').trim();
|
|
301
|
+
if (/\bvite\s+build\b/.test(buildScript)) {
|
|
302
|
+
command = `npm run build -- --base=./`;
|
|
303
|
+
}
|
|
277
304
|
env.VITE_BASE = './';
|
|
278
305
|
}
|
|
279
306
|
|
|
@@ -266,6 +266,13 @@ For NEW projects (team creation only):
|
|
|
266
266
|
Include "projectDir" with a short kebab-case directory name. All agents spawn inside it.
|
|
267
267
|
For EXISTING codebases: Do NOT include "projectDir".
|
|
268
268
|
|
|
269
|
+
DIRECTORY RULES:
|
|
270
|
+
Your team directory (where you are spawned) is for ORCHESTRATION ONLY — .groove/, AGENTS_REGISTRY.md, coordination files. Do NOT instruct agents to create source code, projects, or build artifacts inside the team directory.
|
|
271
|
+
|
|
272
|
+
All source code, features, and projects MUST be created in the PARENT directory (one level up from the team dir). Look at the parent directory structure provided in your context to understand existing directories, naming conventions, and paths. Reference files and directories relative to the parent using "../" prefix in prompts (e.g., "../demo/", "../training/src/").
|
|
273
|
+
|
|
274
|
+
Scope patterns CAN use "../" to reference directories outside the team dir. For multi-directory projects, use patterns like "../demo/**", "../training/**". If the project spans many directories or you are unsure of boundaries, use empty scope ([]) to give agents unrestricted access.
|
|
275
|
+
|
|
269
276
|
MANDATORY RULES:
|
|
270
277
|
|
|
271
278
|
1. For team creation: the LAST entry MUST be { "role": "fullstack", "phase": 2 } — the QC agent.
|
|
@@ -284,6 +291,8 @@ MANDATORY RULES:
|
|
|
284
291
|
|
|
285
292
|
7. In MODE 2, be FAST. Read only the files needed to understand the specific task. Do not re-analyze the full codebase.
|
|
286
293
|
|
|
294
|
+
8. NEVER build source code or project files inside the team directory. The team dir is ephemeral — the user may delete it at any time. All persistent work goes in the parent directory.
|
|
295
|
+
|
|
287
296
|
IMPORTANT: Do not use markdown formatting like ** or ### in your output. Write in plain text with clean formatting. Use line breaks, dashes, and indentation for structure.
|
|
288
297
|
|
|
289
298
|
`,
|
|
@@ -37,6 +37,10 @@ signal.signal(signal.SIGWINCH, resize)
|
|
|
37
37
|
flags = fcntl.fcntl(0, fcntl.F_GETFL)
|
|
38
38
|
fcntl.fcntl(0, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
|
39
39
|
|
|
40
|
+
ESC_START = b'\\x1b]7;'
|
|
41
|
+
ESC_END = b'\\x07'
|
|
42
|
+
stdin_buf = b''
|
|
43
|
+
|
|
40
44
|
try:
|
|
41
45
|
while True:
|
|
42
46
|
rlist = select.select([0, master], [], [], 0.05)[0]
|
|
@@ -44,22 +48,28 @@ try:
|
|
|
44
48
|
try:
|
|
45
49
|
data = os.read(0, 4096)
|
|
46
50
|
if not data: break
|
|
47
|
-
|
|
48
|
-
if b'\\x1b]7;' in data:
|
|
49
|
-
idx = data.index(b'\\x1b]7;')
|
|
50
|
-
end = data.index(b'\\x07', idx)
|
|
51
|
-
params = data[idx+4:end].decode().split(';')
|
|
52
|
-
if len(params) == 2:
|
|
53
|
-
r, c = int(params[0]), int(params[1])
|
|
54
|
-
fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack('HHHH', r, c, 0, 0))
|
|
55
|
-
os.kill(pid, signal.SIGWINCH)
|
|
56
|
-
rest = data[:idx] + data[end+1:]
|
|
57
|
-
if rest:
|
|
58
|
-
os.write(master, rest)
|
|
59
|
-
else:
|
|
60
|
-
os.write(master, data)
|
|
51
|
+
stdin_buf += data
|
|
61
52
|
except OSError as e:
|
|
62
53
|
if e.errno != errno.EAGAIN: break
|
|
54
|
+
# Process buffered stdin — extract resize commands, forward the rest
|
|
55
|
+
while stdin_buf:
|
|
56
|
+
esc_pos = stdin_buf.find(ESC_START)
|
|
57
|
+
if esc_pos == -1:
|
|
58
|
+
os.write(master, stdin_buf)
|
|
59
|
+
stdin_buf = b''
|
|
60
|
+
break
|
|
61
|
+
if esc_pos > 0:
|
|
62
|
+
os.write(master, stdin_buf[:esc_pos])
|
|
63
|
+
stdin_buf = stdin_buf[esc_pos:]
|
|
64
|
+
bel_pos = stdin_buf.find(ESC_END)
|
|
65
|
+
if bel_pos == -1:
|
|
66
|
+
break
|
|
67
|
+
params = stdin_buf[4:bel_pos].decode().split(';')
|
|
68
|
+
if len(params) == 2:
|
|
69
|
+
r, c = int(params[0]), int(params[1])
|
|
70
|
+
fcntl.ioctl(master, termios.TIOCSWINSZ, struct.pack('HHHH', r, c, 0, 0))
|
|
71
|
+
os.kill(pid, signal.SIGWINCH)
|
|
72
|
+
stdin_buf = stdin_buf[bel_pos+1:]
|
|
63
73
|
if master in rlist:
|
|
64
74
|
try:
|
|
65
75
|
data = os.read(master, 4096)
|
|
@@ -146,10 +146,6 @@ export function validateScopePattern(pattern) {
|
|
|
146
146
|
if (pattern.startsWith('/')) {
|
|
147
147
|
throw new Error('Scope patterns cannot be absolute paths');
|
|
148
148
|
}
|
|
149
|
-
// No path traversal
|
|
150
|
-
if (pattern.includes('..')) {
|
|
151
|
-
throw new Error('Scope patterns cannot contain path traversal (..)');
|
|
152
|
-
}
|
|
153
149
|
// No null bytes
|
|
154
150
|
if (pattern.includes('\0')) {
|
|
155
151
|
throw new Error('Scope pattern contains invalid characters');
|