bit-office 1.2.0 → 1.2.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 +573 -413
- package/dist/index.js.map +1 -1
- package/dist/web/404.html +2 -2
- package/dist/web/_next/static/chunks/336.033a979c6c5fe642.js +1 -0
- package/dist/web/_next/static/chunks/368.61fa3954c03d8818.js +1 -0
- package/dist/web/_next/static/chunks/app/office/page-358079be75bf327f.js +1 -0
- package/dist/web/_next/static/chunks/{webpack-1b921ef42040f4d7.js → webpack-1c3347ded5c6a52b.js} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/index.txt +1 -1
- package/dist/web/join.html +2 -2
- package/dist/web/join.txt +1 -1
- package/dist/web/office.html +3 -3
- package/dist/web/office.txt +2 -2
- package/dist/web/pair.html +2 -2
- package/dist/web/pair.txt +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/336.3b48469aad2c6417.js +0 -1
- package/dist/web/_next/static/chunks/368.5516163baaf54ba9.js +0 -1
- package/dist/web/_next/static/chunks/app/office/page-31d54910f922915a.js +0 -1
- /package/dist/web/_next/static/{7Qn9ZRObqct6tz2cPYAn5 → 0Clbb0V7-cqhwpr3pjKk1}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{7Qn9ZRObqct6tz2cPYAn5 → 0Clbb0V7-cqhwpr3pjKk1}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -4201,6 +4201,11 @@ var SuggestCommand = external_exports.object({
|
|
|
4201
4201
|
text: external_exports.string().max(500),
|
|
4202
4202
|
author: external_exports.string().max(30).optional()
|
|
4203
4203
|
});
|
|
4204
|
+
var RateProjectCommand = external_exports.object({
|
|
4205
|
+
type: external_exports.literal("RATE_PROJECT"),
|
|
4206
|
+
projectId: external_exports.string().optional(),
|
|
4207
|
+
ratings: external_exports.record(external_exports.string(), external_exports.number().min(1).max(5))
|
|
4208
|
+
});
|
|
4204
4209
|
var ListProjectsCommand = external_exports.object({
|
|
4205
4210
|
type: external_exports.literal("LIST_PROJECTS")
|
|
4206
4211
|
});
|
|
@@ -4226,6 +4231,7 @@ var CommandSchema = external_exports.discriminatedUnion("type", [
|
|
|
4226
4231
|
SaveAgentDefCommand,
|
|
4227
4232
|
DeleteAgentDefCommand,
|
|
4228
4233
|
SuggestCommand,
|
|
4234
|
+
RateProjectCommand,
|
|
4229
4235
|
ListProjectsCommand,
|
|
4230
4236
|
LoadProjectCommand
|
|
4231
4237
|
]);
|
|
@@ -4391,7 +4397,9 @@ var ProjectListEvent = external_exports.object({
|
|
|
4391
4397
|
endedAt: external_exports.number(),
|
|
4392
4398
|
agentNames: external_exports.array(external_exports.string()),
|
|
4393
4399
|
eventCount: external_exports.number(),
|
|
4394
|
-
preview: ProjectPreviewSchema
|
|
4400
|
+
preview: ProjectPreviewSchema,
|
|
4401
|
+
tokenUsage: external_exports.object({ inputTokens: external_exports.number(), outputTokens: external_exports.number() }).optional(),
|
|
4402
|
+
ratings: external_exports.record(external_exports.string(), external_exports.number()).optional()
|
|
4395
4403
|
}))
|
|
4396
4404
|
});
|
|
4397
4405
|
var ProjectDataEvent = external_exports.object({
|
|
@@ -4402,6 +4410,10 @@ var ProjectDataEvent = external_exports.object({
|
|
|
4402
4410
|
endedAt: external_exports.number(),
|
|
4403
4411
|
events: external_exports.array(external_exports.any())
|
|
4404
4412
|
});
|
|
4413
|
+
var PreviewReadyEvent = external_exports.object({
|
|
4414
|
+
type: external_exports.literal("PREVIEW_READY"),
|
|
4415
|
+
url: external_exports.string()
|
|
4416
|
+
});
|
|
4405
4417
|
var GatewayEventSchema = external_exports.discriminatedUnion("type", [
|
|
4406
4418
|
AgentsSyncEvent,
|
|
4407
4419
|
AgentStatusEvent,
|
|
@@ -4421,7 +4433,8 @@ var GatewayEventSchema = external_exports.discriminatedUnion("type", [
|
|
|
4421
4433
|
AgentDefsEvent,
|
|
4422
4434
|
SuggestionEvent,
|
|
4423
4435
|
ProjectListEvent,
|
|
4424
|
-
ProjectDataEvent
|
|
4436
|
+
ProjectDataEvent,
|
|
4437
|
+
PreviewReadyEvent
|
|
4425
4438
|
]);
|
|
4426
4439
|
|
|
4427
4440
|
// ../../packages/shared/src/presets.ts
|
|
@@ -5335,14 +5348,14 @@ var CONFIG = {
|
|
|
5335
5348
|
};
|
|
5336
5349
|
|
|
5337
5350
|
// ../../packages/orchestrator/src/agent-session.ts
|
|
5338
|
-
import { spawn
|
|
5351
|
+
import { spawn, execSync as execSync2 } from "child_process";
|
|
5339
5352
|
import path5 from "path";
|
|
5340
|
-
import { readFileSync as
|
|
5341
|
-
import { homedir as
|
|
5353
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
|
|
5354
|
+
import { homedir as homedir5 } from "os";
|
|
5342
5355
|
|
|
5343
5356
|
// ../../packages/orchestrator/src/preview-resolver.ts
|
|
5344
|
-
import { existsSync as
|
|
5345
|
-
import
|
|
5357
|
+
import { existsSync as existsSync5 } from "fs";
|
|
5358
|
+
import path3 from "path";
|
|
5346
5359
|
|
|
5347
5360
|
// ../../packages/orchestrator/src/resolve-path.ts
|
|
5348
5361
|
import path2 from "path";
|
|
@@ -5362,157 +5375,19 @@ function resolveAgentPath(filePath, projectDir, workspace) {
|
|
|
5362
5375
|
return void 0;
|
|
5363
5376
|
}
|
|
5364
5377
|
|
|
5365
|
-
// ../../packages/orchestrator/src/preview-server.ts
|
|
5366
|
-
import { spawn, execSync as execSync2 } from "child_process";
|
|
5367
|
-
import { existsSync as existsSync5 } from "fs";
|
|
5368
|
-
import path3 from "path";
|
|
5369
|
-
var STATIC_PORT = 9100;
|
|
5370
|
-
var PreviewServer = class {
|
|
5371
|
-
process = null;
|
|
5372
|
-
currentDir = null;
|
|
5373
|
-
isDetached = false;
|
|
5374
|
-
/**
|
|
5375
|
-
* Mode 1: Serve a static file directory on a fixed port.
|
|
5376
|
-
* Returns the preview URL for the given file.
|
|
5377
|
-
*/
|
|
5378
|
-
serve(filePath) {
|
|
5379
|
-
if (!existsSync5(filePath)) {
|
|
5380
|
-
console.log(`[PreviewServer] File not found: ${filePath}`);
|
|
5381
|
-
return void 0;
|
|
5382
|
-
}
|
|
5383
|
-
const dir = path3.dirname(filePath);
|
|
5384
|
-
const fileName = path3.basename(filePath);
|
|
5385
|
-
this.stop();
|
|
5386
|
-
try {
|
|
5387
|
-
this.process = spawn("npx", ["serve", dir, "-l", String(STATIC_PORT), "--no-clipboard"], {
|
|
5388
|
-
stdio: "ignore",
|
|
5389
|
-
detached: true
|
|
5390
|
-
});
|
|
5391
|
-
this.process.unref();
|
|
5392
|
-
this.currentDir = dir;
|
|
5393
|
-
this.isDetached = true;
|
|
5394
|
-
const url = `http://localhost:${STATIC_PORT}/${fileName}`;
|
|
5395
|
-
console.log(`[PreviewServer] Serving ${dir} on port ${STATIC_PORT}`);
|
|
5396
|
-
return url;
|
|
5397
|
-
} catch (e) {
|
|
5398
|
-
console.log(`[PreviewServer] Failed to start static serve: ${e}`);
|
|
5399
|
-
return void 0;
|
|
5400
|
-
}
|
|
5401
|
-
}
|
|
5402
|
-
/**
|
|
5403
|
-
* Mode 2: Run a command (e.g. "python app.py") and use the specified port.
|
|
5404
|
-
* The command is expected to start a server on the given port.
|
|
5405
|
-
* Returns the preview URL.
|
|
5406
|
-
*/
|
|
5407
|
-
runCommand(cmd, cwd, port) {
|
|
5408
|
-
this.stop();
|
|
5409
|
-
try {
|
|
5410
|
-
this.process = spawn(cmd, {
|
|
5411
|
-
shell: true,
|
|
5412
|
-
cwd,
|
|
5413
|
-
stdio: "ignore",
|
|
5414
|
-
detached: true
|
|
5415
|
-
});
|
|
5416
|
-
this.process.unref();
|
|
5417
|
-
this.currentDir = cwd;
|
|
5418
|
-
this.isDetached = true;
|
|
5419
|
-
const url = `http://localhost:${port}`;
|
|
5420
|
-
console.log(`[PreviewServer] Running "${cmd}" in ${cwd}, preview at port ${port}`);
|
|
5421
|
-
return url;
|
|
5422
|
-
} catch (e) {
|
|
5423
|
-
console.log(`[PreviewServer] Failed to run command: ${e}`);
|
|
5424
|
-
return void 0;
|
|
5425
|
-
}
|
|
5426
|
-
}
|
|
5427
|
-
/**
|
|
5428
|
-
* Mode 3: Launch a desktop/CLI process (no web preview URL).
|
|
5429
|
-
* Used for Pygame, Tkinter, Electron, terminal apps, etc.
|
|
5430
|
-
* NOT detached — GUI apps need the login session to access WindowServer (macOS).
|
|
5431
|
-
*/
|
|
5432
|
-
launchProcess(cmd, cwd) {
|
|
5433
|
-
this.stop();
|
|
5434
|
-
try {
|
|
5435
|
-
this.process = spawn(cmd, {
|
|
5436
|
-
shell: true,
|
|
5437
|
-
cwd,
|
|
5438
|
-
stdio: ["ignore", "ignore", "pipe"]
|
|
5439
|
-
});
|
|
5440
|
-
this.currentDir = cwd;
|
|
5441
|
-
this.isDetached = false;
|
|
5442
|
-
console.log(`[PreviewServer] Launched "${cmd}" in ${cwd} (pid=${this.process.pid})`);
|
|
5443
|
-
this.process.stderr?.on("data", (data) => {
|
|
5444
|
-
const msg = data.toString().trim();
|
|
5445
|
-
if (msg) console.log(`[PreviewServer] stderr: ${msg.slice(0, 200)}`);
|
|
5446
|
-
});
|
|
5447
|
-
this.process.on("exit", (code) => {
|
|
5448
|
-
console.log(`[PreviewServer] Process exited with code ${code}`);
|
|
5449
|
-
});
|
|
5450
|
-
} catch (e) {
|
|
5451
|
-
console.log(`[PreviewServer] Failed to launch process: ${e}`);
|
|
5452
|
-
}
|
|
5453
|
-
}
|
|
5454
|
-
/** Kill the current process and any orphan process on the static port */
|
|
5455
|
-
stop() {
|
|
5456
|
-
if (this.process) {
|
|
5457
|
-
try {
|
|
5458
|
-
if (this.isDetached && this.process.pid) {
|
|
5459
|
-
process.kill(-this.process.pid, "SIGTERM");
|
|
5460
|
-
} else {
|
|
5461
|
-
this.process.kill("SIGTERM");
|
|
5462
|
-
}
|
|
5463
|
-
} catch {
|
|
5464
|
-
try {
|
|
5465
|
-
this.process.kill("SIGTERM");
|
|
5466
|
-
} catch {
|
|
5467
|
-
}
|
|
5468
|
-
}
|
|
5469
|
-
this.process = null;
|
|
5470
|
-
this.currentDir = null;
|
|
5471
|
-
this.isDetached = false;
|
|
5472
|
-
console.log(`[PreviewServer] Stopped`);
|
|
5473
|
-
}
|
|
5474
|
-
this.killPortHolder(STATIC_PORT);
|
|
5475
|
-
}
|
|
5476
|
-
/** Kill whatever process is listening on the given port (best-effort). */
|
|
5477
|
-
killPortHolder(port) {
|
|
5478
|
-
try {
|
|
5479
|
-
const out = execSync2(`lsof -ti :${port}`, { encoding: "utf-8", timeout: 3e3 }).trim();
|
|
5480
|
-
if (out) {
|
|
5481
|
-
for (const pid of out.split("\n")) {
|
|
5482
|
-
const n = parseInt(pid, 10);
|
|
5483
|
-
if (n > 0) {
|
|
5484
|
-
try {
|
|
5485
|
-
process.kill(n, "SIGKILL");
|
|
5486
|
-
} catch {
|
|
5487
|
-
}
|
|
5488
|
-
}
|
|
5489
|
-
}
|
|
5490
|
-
console.log(`[PreviewServer] Killed orphan process(es) on port ${port}: ${out.replace(/\n/g, ", ")}`);
|
|
5491
|
-
}
|
|
5492
|
-
} catch {
|
|
5493
|
-
}
|
|
5494
|
-
}
|
|
5495
|
-
};
|
|
5496
|
-
var previewServer = new PreviewServer();
|
|
5497
|
-
|
|
5498
5378
|
// ../../packages/orchestrator/src/preview-resolver.ts
|
|
5499
5379
|
var EMPTY = { previewUrl: void 0, previewPath: void 0 };
|
|
5500
5380
|
function resolvePreview(input) {
|
|
5501
5381
|
const { cwd, workspace } = input;
|
|
5502
5382
|
if (input.previewCmd && input.previewPort) {
|
|
5503
|
-
|
|
5504
|
-
if (url) return { previewUrl: url, previewPath: void 0 };
|
|
5383
|
+
return EMPTY;
|
|
5505
5384
|
}
|
|
5506
5385
|
if (input.previewCmd && !input.previewPort) {
|
|
5507
|
-
console.log(`[PreviewResolver] Desktop app ready (user can Launch): ${input.previewCmd}`);
|
|
5508
5386
|
return EMPTY;
|
|
5509
5387
|
}
|
|
5510
5388
|
if (input.entryFile && /\.html?$/i.test(input.entryFile)) {
|
|
5511
5389
|
const absPath = resolveAgentPath(input.entryFile, cwd, workspace);
|
|
5512
|
-
if (absPath) {
|
|
5513
|
-
const url = previewServer.serve(absPath);
|
|
5514
|
-
if (url) return { previewUrl: url, previewPath: absPath };
|
|
5515
|
-
}
|
|
5390
|
+
if (absPath) return { previewUrl: void 0, previewPath: absPath };
|
|
5516
5391
|
}
|
|
5517
5392
|
if (input.stdout) {
|
|
5518
5393
|
const match = input.stdout.match(/PREVIEW:\s*(https?:\/\/[^\s*)\]>]+)/i);
|
|
@@ -5524,28 +5399,19 @@ function resolvePreview(input) {
|
|
|
5524
5399
|
const fileMatch = input.stdout.match(/(?:open\s+)?((?:\/[\w./_-]+|[\w./_-]+)\.html?)\b/i);
|
|
5525
5400
|
if (fileMatch) {
|
|
5526
5401
|
const absPath = resolveAgentPath(fileMatch[1], cwd, workspace);
|
|
5527
|
-
if (absPath) {
|
|
5528
|
-
const url = previewServer.serve(absPath);
|
|
5529
|
-
if (url) return { previewUrl: url, previewPath: absPath };
|
|
5530
|
-
}
|
|
5402
|
+
if (absPath) return { previewUrl: void 0, previewPath: absPath };
|
|
5531
5403
|
}
|
|
5532
5404
|
}
|
|
5533
5405
|
if (input.changedFiles) {
|
|
5534
5406
|
for (const f of input.changedFiles) {
|
|
5535
5407
|
if (!/\.html?$/i.test(f)) continue;
|
|
5536
5408
|
const absPath = resolveAgentPath(f, cwd, workspace);
|
|
5537
|
-
if (absPath) {
|
|
5538
|
-
const url = previewServer.serve(absPath);
|
|
5539
|
-
if (url) return { previewUrl: url, previewPath: absPath };
|
|
5540
|
-
}
|
|
5409
|
+
if (absPath) return { previewUrl: void 0, previewPath: absPath };
|
|
5541
5410
|
}
|
|
5542
5411
|
}
|
|
5543
5412
|
for (const candidate of CONFIG.preview.buildOutputCandidates) {
|
|
5544
|
-
const absPath =
|
|
5545
|
-
if (
|
|
5546
|
-
const url = previewServer.serve(absPath);
|
|
5547
|
-
if (url) return { previewUrl: url, previewPath: absPath };
|
|
5548
|
-
}
|
|
5413
|
+
const absPath = path3.join(cwd, candidate);
|
|
5414
|
+
if (existsSync5(absPath)) return { previewUrl: void 0, previewPath: absPath };
|
|
5549
5415
|
}
|
|
5550
5416
|
return EMPTY;
|
|
5551
5417
|
}
|
|
@@ -5559,19 +5425,23 @@ function parseAgentOutput(raw, fallbackText) {
|
|
|
5559
5425
|
const entryFileMatch = text.match(/ENTRY_FILE:\s*(.+)/i);
|
|
5560
5426
|
const projectDirMatch = text.match(/PROJECT_DIR:\s*(.+)/i);
|
|
5561
5427
|
const previewCmdMatch = text.match(/PREVIEW_CMD:\s*(.+)/i);
|
|
5562
|
-
const previewPortMatch = text.match(/PREVIEW_PORT:\s*(\d+)/i);
|
|
5428
|
+
const previewPortMatch = text.match(/PREVIEW_PORT:\s*[*`_]*(\d+)/i);
|
|
5429
|
+
const stripMarkdown = (v) => v.replace(/\*\*/g, "").replace(/`/g, "").replace(/^_+|_+$/g, "").trim();
|
|
5563
5430
|
const changedFiles = [];
|
|
5564
5431
|
if (filesMatch) {
|
|
5565
5432
|
const fileList = filesMatch[1].trim();
|
|
5566
5433
|
for (const f of fileList.split(/[,\n]+/)) {
|
|
5567
|
-
const cleaned = f.trim().replace(/^[-*]\s*/, "");
|
|
5434
|
+
const cleaned = stripMarkdown(f.trim().replace(/^[-*]\s*/, ""));
|
|
5568
5435
|
if (cleaned) changedFiles.push(cleaned);
|
|
5569
5436
|
}
|
|
5570
5437
|
}
|
|
5571
5438
|
const isPlaceholder = (v) => !v || /^[\[(].*not provided.*[\])]$/i.test(v) || /^[\[(].*n\/?a.*[\])]$/i.test(v) || /^none$/i.test(v);
|
|
5572
|
-
const
|
|
5573
|
-
const
|
|
5574
|
-
const
|
|
5439
|
+
const rawEntry = entryFileMatch?.[1]?.trim();
|
|
5440
|
+
const rawDir = projectDirMatch?.[1]?.trim();
|
|
5441
|
+
const rawCmd = previewCmdMatch?.[1]?.trim();
|
|
5442
|
+
const entryFile = isPlaceholder(rawEntry) ? void 0 : stripMarkdown(rawEntry);
|
|
5443
|
+
const projectDir = isPlaceholder(rawDir) ? void 0 : stripMarkdown(rawDir);
|
|
5444
|
+
const previewCmd = isPlaceholder(rawCmd) ? void 0 : stripMarkdown(rawCmd);
|
|
5575
5445
|
const previewPort = previewPortMatch ? parseInt(previewPortMatch[1], 10) : void 0;
|
|
5576
5446
|
if (summaryMatch) {
|
|
5577
5447
|
return { summary: summaryMatch[1].trim(), fullOutput, changedFiles, entryFile, projectDir, previewCmd, previewPort };
|
|
@@ -5611,10 +5481,139 @@ function extractFallbackSummary(raw, _hasFiles, _entryFile, _projectDir) {
|
|
|
5611
5481
|
|
|
5612
5482
|
// ../../packages/orchestrator/src/agent-session.ts
|
|
5613
5483
|
import { nanoid as nanoid3 } from "nanoid";
|
|
5614
|
-
|
|
5484
|
+
|
|
5485
|
+
// ../../packages/orchestrator/src/memory.ts
|
|
5486
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
|
|
5487
|
+
import path4 from "path";
|
|
5488
|
+
import { homedir as homedir4 } from "os";
|
|
5489
|
+
var MEMORY_DIR = path4.join(homedir4(), ".bit-office", "memory");
|
|
5490
|
+
function ensureDir() {
|
|
5491
|
+
if (!existsSync6(MEMORY_DIR)) {
|
|
5492
|
+
mkdirSync4(MEMORY_DIR, { recursive: true });
|
|
5493
|
+
}
|
|
5494
|
+
}
|
|
5495
|
+
function loadStore() {
|
|
5496
|
+
const filePath = path4.join(MEMORY_DIR, "memory.json");
|
|
5497
|
+
try {
|
|
5498
|
+
if (existsSync6(filePath)) {
|
|
5499
|
+
return JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
5500
|
+
}
|
|
5501
|
+
} catch {
|
|
5502
|
+
}
|
|
5503
|
+
return { reviewPatterns: [], techPreferences: [], projectHistory: [] };
|
|
5504
|
+
}
|
|
5505
|
+
function saveStore(store) {
|
|
5506
|
+
ensureDir();
|
|
5507
|
+
const filePath = path4.join(MEMORY_DIR, "memory.json");
|
|
5508
|
+
writeFileSync4(filePath, JSON.stringify(store, null, 2), "utf-8");
|
|
5509
|
+
}
|
|
5510
|
+
function recordReviewFeedback(reviewOutput) {
|
|
5511
|
+
const verdictMatch = reviewOutput.match(/VERDICT[:\s]*(\w+)/i);
|
|
5512
|
+
if (!verdictMatch || verdictMatch[1].toUpperCase() !== "FAIL") return;
|
|
5513
|
+
const issueLines = [];
|
|
5514
|
+
const issueRe = /^\s*\d+[.)]\s*(.+)/gm;
|
|
5515
|
+
let match;
|
|
5516
|
+
while ((match = issueRe.exec(reviewOutput)) !== null) {
|
|
5517
|
+
const issue = match[1].trim();
|
|
5518
|
+
if (issue.length > 10 && issue.length < 200) {
|
|
5519
|
+
issueLines.push(issue);
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
if (issueLines.length === 0) return;
|
|
5523
|
+
const store = loadStore();
|
|
5524
|
+
const now = Date.now();
|
|
5525
|
+
for (const issue of issueLines) {
|
|
5526
|
+
const normalized = normalizeIssue(issue);
|
|
5527
|
+
const existing = store.reviewPatterns.find((p) => normalizeIssue(p.pattern) === normalized);
|
|
5528
|
+
if (existing) {
|
|
5529
|
+
existing.count++;
|
|
5530
|
+
existing.lastSeen = now;
|
|
5531
|
+
} else {
|
|
5532
|
+
store.reviewPatterns.push({ pattern: issue, count: 1, lastSeen: now });
|
|
5533
|
+
}
|
|
5534
|
+
}
|
|
5535
|
+
store.reviewPatterns.sort((a, b) => b.count - a.count);
|
|
5536
|
+
store.reviewPatterns = store.reviewPatterns.slice(0, 20);
|
|
5537
|
+
saveStore(store);
|
|
5538
|
+
console.log(`[Memory] Recorded ${issueLines.length} review pattern(s), total=${store.reviewPatterns.length}`);
|
|
5539
|
+
}
|
|
5540
|
+
function recordProjectCompletion(summary, tech, reviewPassed) {
|
|
5541
|
+
const store = loadStore();
|
|
5542
|
+
store.projectHistory.push({
|
|
5543
|
+
summary: summary.slice(0, 300),
|
|
5544
|
+
tech: tech.slice(0, 100),
|
|
5545
|
+
completedAt: Date.now(),
|
|
5546
|
+
reviewPassed
|
|
5547
|
+
});
|
|
5548
|
+
if (store.projectHistory.length > 50) {
|
|
5549
|
+
store.projectHistory = store.projectHistory.slice(-50);
|
|
5550
|
+
}
|
|
5551
|
+
saveStore(store);
|
|
5552
|
+
console.log(`[Memory] Recorded project completion: ${summary.slice(0, 80)}`);
|
|
5553
|
+
}
|
|
5554
|
+
function recordTechPreference(tech) {
|
|
5555
|
+
const store = loadStore();
|
|
5556
|
+
const normalized = tech.trim().toLowerCase();
|
|
5557
|
+
if (!store.techPreferences.some((t) => t.toLowerCase() === normalized)) {
|
|
5558
|
+
store.techPreferences.push(tech.trim());
|
|
5559
|
+
if (store.techPreferences.length > 10) {
|
|
5560
|
+
store.techPreferences = store.techPreferences.slice(-10);
|
|
5561
|
+
}
|
|
5562
|
+
saveStore(store);
|
|
5563
|
+
console.log(`[Memory] Recorded tech preference: ${tech}`);
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
function recordProjectRatings(ratings) {
|
|
5567
|
+
const store = loadStore();
|
|
5568
|
+
if (store.projectHistory.length === 0) return;
|
|
5569
|
+
store.projectHistory[store.projectHistory.length - 1].ratings = ratings;
|
|
5570
|
+
saveStore(store);
|
|
5571
|
+
const avg = Object.values(ratings);
|
|
5572
|
+
const mean = avg.length > 0 ? (avg.reduce((a, b) => a + b, 0) / avg.length).toFixed(1) : "?";
|
|
5573
|
+
console.log(`[Memory] Updated latest project ratings (avg ${mean}/5)`);
|
|
5574
|
+
}
|
|
5575
|
+
function getMemoryContext() {
|
|
5576
|
+
const store = loadStore();
|
|
5577
|
+
const sections = [];
|
|
5578
|
+
const recurring = store.reviewPatterns.filter((p) => p.count >= 2);
|
|
5579
|
+
if (recurring.length > 0) {
|
|
5580
|
+
const lines = recurring.slice(0, 5).map((p) => `- ${p.pattern} (flagged ${p.count}x)`);
|
|
5581
|
+
sections.push(`COMMON REVIEW ISSUES (avoid these):
|
|
5582
|
+
${lines.join("\n")}`);
|
|
5583
|
+
}
|
|
5584
|
+
if (store.techPreferences.length > 0) {
|
|
5585
|
+
const recent = store.techPreferences.slice(-3);
|
|
5586
|
+
sections.push(`USER'S PREFERRED TECH: ${recent.join(", ")}`);
|
|
5587
|
+
}
|
|
5588
|
+
const rated = store.projectHistory.filter((p) => p.ratings && Object.keys(p.ratings).length > 0).slice(-3);
|
|
5589
|
+
if (rated.length > 0) {
|
|
5590
|
+
const lines = rated.map((p) => {
|
|
5591
|
+
const r = p.ratings;
|
|
5592
|
+
const scores = Object.entries(r).map(([k, v]) => `${k}:${v}/5`).join(", ");
|
|
5593
|
+
const avg = Object.values(r).reduce((a, b) => a + b, 0) / Object.values(r).length;
|
|
5594
|
+
const weak = Object.entries(r).filter(([, v]) => v <= 2).map(([k]) => k);
|
|
5595
|
+
let line = `- "${p.summary.slice(0, 60)}" [${scores}] avg=${avg.toFixed(1)}`;
|
|
5596
|
+
if (weak.length > 0) line += ` \u2192 improve: ${weak.join(", ")}`;
|
|
5597
|
+
return line;
|
|
5598
|
+
});
|
|
5599
|
+
sections.push(`PAST PROJECT RATINGS (learn from user feedback):
|
|
5600
|
+
${lines.join("\n")}`);
|
|
5601
|
+
}
|
|
5602
|
+
if (sections.length === 0) return "";
|
|
5603
|
+
return `
|
|
5604
|
+
===== LEARNED FROM PREVIOUS PROJECTS =====
|
|
5605
|
+
${sections.join("\n\n")}
|
|
5606
|
+
`;
|
|
5607
|
+
}
|
|
5608
|
+
function normalizeIssue(issue) {
|
|
5609
|
+
return issue.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, " ").trim();
|
|
5610
|
+
}
|
|
5611
|
+
|
|
5612
|
+
// ../../packages/orchestrator/src/agent-session.ts
|
|
5613
|
+
var SESSION_FILE = path5.join(homedir5(), ".bit-office", "agent-sessions.json");
|
|
5615
5614
|
function loadSessionMap() {
|
|
5616
5615
|
try {
|
|
5617
|
-
if (existsSync7(SESSION_FILE)) return JSON.parse(
|
|
5616
|
+
if (existsSync7(SESSION_FILE)) return JSON.parse(readFileSync5(SESSION_FILE, "utf-8"));
|
|
5618
5617
|
} catch {
|
|
5619
5618
|
}
|
|
5620
5619
|
return {};
|
|
@@ -5624,14 +5623,14 @@ function clearSessionId(agentId) {
|
|
|
5624
5623
|
}
|
|
5625
5624
|
function saveSessionId(agentId, sessionId) {
|
|
5626
5625
|
const dir = path5.dirname(SESSION_FILE);
|
|
5627
|
-
if (!existsSync7(dir))
|
|
5626
|
+
if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
|
|
5628
5627
|
const map = loadSessionMap();
|
|
5629
5628
|
if (sessionId) {
|
|
5630
5629
|
map[agentId] = sessionId;
|
|
5631
5630
|
} else {
|
|
5632
5631
|
delete map[agentId];
|
|
5633
5632
|
}
|
|
5634
|
-
|
|
5633
|
+
writeFileSync5(SESSION_FILE, JSON.stringify(map), "utf-8");
|
|
5635
5634
|
}
|
|
5636
5635
|
var AgentSession = class {
|
|
5637
5636
|
agentId;
|
|
@@ -5656,6 +5655,8 @@ var AgentSession = class {
|
|
|
5656
5655
|
stderrBuffer = "";
|
|
5657
5656
|
taskInputTokens = 0;
|
|
5658
5657
|
taskOutputTokens = 0;
|
|
5658
|
+
/** Dedup same-turn repeated usage in assistant messages */
|
|
5659
|
+
lastUsageSignature = "";
|
|
5659
5660
|
hasHistory;
|
|
5660
5661
|
sessionId;
|
|
5661
5662
|
taskQueue = [];
|
|
@@ -5761,6 +5762,7 @@ var AgentSession = class {
|
|
|
5761
5762
|
this.stderrBuffer = "";
|
|
5762
5763
|
this.taskInputTokens = 0;
|
|
5763
5764
|
this.taskOutputTokens = 0;
|
|
5765
|
+
this.lastUsageSignature = "";
|
|
5764
5766
|
this.onEvent({
|
|
5765
5767
|
type: "task:started",
|
|
5766
5768
|
agentId: this.agentId,
|
|
@@ -5782,7 +5784,7 @@ var AgentSession = class {
|
|
|
5782
5784
|
teamRoster: teamContext ?? "",
|
|
5783
5785
|
originalTask,
|
|
5784
5786
|
prompt,
|
|
5785
|
-
memory: this._memoryContext,
|
|
5787
|
+
memory: this._memoryContext || getMemoryContext(),
|
|
5786
5788
|
soloHint: this.teamId ? "" : `- You are a SOLO developer. Do NOT delegate, assign tasks, or mention other team members. Do ALL the work yourself.
|
|
5787
5789
|
- PROJECT DIRECTORY: When creating files, first create a dedicated project directory (short kebab-case name, e.g. "snake-game"). Do ALL work inside it. Report it as PROJECT_DIR: <directory-name> in your output. If the user is just chatting (no code needed), skip this.`
|
|
5788
5790
|
};
|
|
@@ -5812,12 +5814,12 @@ var AgentSession = class {
|
|
|
5812
5814
|
skipResume: isFirstExecute && this.hasHistory
|
|
5813
5815
|
});
|
|
5814
5816
|
try {
|
|
5815
|
-
const whichPath =
|
|
5817
|
+
const whichPath = execSync2(`which ${this.backend.command}`, { env: cleanEnv, encoding: "utf-8", timeout: 3e3 }).trim();
|
|
5816
5818
|
console.log(`[Agent ${this.name}] Binary: ${whichPath}, CLAUDECODE=${cleanEnv.CLAUDECODE ?? "unset"}, ENTRYPOINT=${cleanEnv.CLAUDE_CODE_ENTRYPOINT ?? "unset"}`);
|
|
5817
5819
|
} catch {
|
|
5818
5820
|
}
|
|
5819
5821
|
console.log(`[Agent ${this.name}] Spawning: ${this.backend.command} ${args.map((a) => a.length > 80 ? a.slice(0, 80) + "..." : a).join(" ")}`);
|
|
5820
|
-
this.process =
|
|
5822
|
+
this.process = spawn(this.backend.command, args, {
|
|
5821
5823
|
cwd,
|
|
5822
5824
|
env: cleanEnv,
|
|
5823
5825
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -5910,8 +5912,20 @@ var AgentSession = class {
|
|
|
5910
5912
|
if (msg.type === "assistant" && msg.message?.content) {
|
|
5911
5913
|
if (msg.message.usage) {
|
|
5912
5914
|
const usage = msg.message.usage;
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
+
const turnIn = (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0);
|
|
5916
|
+
const turnOut = usage.output_tokens ?? 0;
|
|
5917
|
+
const sig = `${turnIn}:${turnOut}`;
|
|
5918
|
+
if (sig !== this.lastUsageSignature) {
|
|
5919
|
+
this.lastUsageSignature = sig;
|
|
5920
|
+
this.taskInputTokens += turnIn;
|
|
5921
|
+
this.taskOutputTokens += turnOut;
|
|
5922
|
+
this.onEvent({
|
|
5923
|
+
type: "token:update",
|
|
5924
|
+
agentId: this.agentId,
|
|
5925
|
+
inputTokens: this.taskInputTokens,
|
|
5926
|
+
outputTokens: this.taskOutputTokens
|
|
5927
|
+
});
|
|
5928
|
+
}
|
|
5915
5929
|
}
|
|
5916
5930
|
for (const block of msg.message.content) {
|
|
5917
5931
|
if (block.type === "text" && block.text) {
|
|
@@ -5922,12 +5936,27 @@ var AgentSession = class {
|
|
|
5922
5936
|
console.log(`[Agent ${this.name} thinking] ${block.thinking.slice(0, 120)}...`);
|
|
5923
5937
|
}
|
|
5924
5938
|
}
|
|
5925
|
-
} else if (msg.type === "result"
|
|
5926
|
-
if (
|
|
5927
|
-
|
|
5928
|
-
|
|
5939
|
+
} else if (msg.type === "result") {
|
|
5940
|
+
if (msg.usage) {
|
|
5941
|
+
const usage = msg.usage;
|
|
5942
|
+
const totalIn = (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0);
|
|
5943
|
+
const totalOut = usage.output_tokens ?? 0;
|
|
5944
|
+
this.taskInputTokens = totalIn;
|
|
5945
|
+
this.taskOutputTokens = totalOut;
|
|
5946
|
+
this.onEvent({
|
|
5947
|
+
type: "token:update",
|
|
5948
|
+
agentId: this.agentId,
|
|
5949
|
+
inputTokens: this.taskInputTokens,
|
|
5950
|
+
outputTokens: this.taskOutputTokens
|
|
5951
|
+
});
|
|
5952
|
+
}
|
|
5953
|
+
if (msg.result) {
|
|
5954
|
+
if (!this.stdoutBuffer) {
|
|
5955
|
+
this.stdoutBuffer = msg.result;
|
|
5956
|
+
handleTextLine(msg.result);
|
|
5957
|
+
}
|
|
5958
|
+
this._lastResultText = msg.result;
|
|
5929
5959
|
}
|
|
5930
|
-
this._lastResultText = msg.result;
|
|
5931
5960
|
}
|
|
5932
5961
|
continue;
|
|
5933
5962
|
} catch {
|
|
@@ -5948,11 +5977,18 @@ var AgentSession = class {
|
|
|
5948
5977
|
}
|
|
5949
5978
|
});
|
|
5950
5979
|
this.process.on("close", (code) => {
|
|
5980
|
+
const agentPid = this.process?.pid;
|
|
5951
5981
|
this.process = null;
|
|
5952
5982
|
if (this.taskTimeout) {
|
|
5953
5983
|
clearTimeout(this.taskTimeout);
|
|
5954
5984
|
this.taskTimeout = null;
|
|
5955
5985
|
}
|
|
5986
|
+
if (agentPid) {
|
|
5987
|
+
try {
|
|
5988
|
+
process.kill(-agentPid, "SIGTERM");
|
|
5989
|
+
} catch {
|
|
5990
|
+
}
|
|
5991
|
+
}
|
|
5956
5992
|
const remaining = jsonLineBuf.trim();
|
|
5957
5993
|
if (remaining) {
|
|
5958
5994
|
jsonLineBuf = "";
|
|
@@ -6315,6 +6351,8 @@ var DelegationRouter = class {
|
|
|
6315
6351
|
devFixAttempts = /* @__PURE__ */ new Map();
|
|
6316
6352
|
/** Tracks which dev agent was last assigned to work (for reviewer → dev routing) */
|
|
6317
6353
|
lastDevAgentId = null;
|
|
6354
|
+
/** Last known preview fields from developer output (survives across rounds for leader context) */
|
|
6355
|
+
lastDevPreview = "";
|
|
6318
6356
|
agentManager;
|
|
6319
6357
|
promptEngine;
|
|
6320
6358
|
emitEvent;
|
|
@@ -6418,6 +6456,7 @@ var DelegationRouter = class {
|
|
|
6418
6456
|
this.teamProjectDir = null;
|
|
6419
6457
|
this.devFixAttempts.clear();
|
|
6420
6458
|
this.lastDevAgentId = null;
|
|
6459
|
+
this.lastDevPreview = "";
|
|
6421
6460
|
for (const pending of this.pendingResults.values()) {
|
|
6422
6461
|
clearTimeout(pending.timer);
|
|
6423
6462
|
}
|
|
@@ -6596,6 +6635,17 @@ SUMMARY: (one sentence overall assessment)`
|
|
|
6596
6635
|
if (this.tryDirectFix(agentId, fromSession, fullOutput ?? summary, originAgentId)) {
|
|
6597
6636
|
return;
|
|
6598
6637
|
}
|
|
6638
|
+
const fromRole = fromSession?.role?.toLowerCase() ?? "";
|
|
6639
|
+
if (!fromRole.includes("review") && fullOutput) {
|
|
6640
|
+
const lines = [];
|
|
6641
|
+
const em = fullOutput.match(/ENTRY_FILE:\s*(.+)/i);
|
|
6642
|
+
const cm = fullOutput.match(/PREVIEW_CMD:\s*(.+)/i);
|
|
6643
|
+
const pm = fullOutput.match(/PREVIEW_PORT:\s*[*`_]*(\d+)/i);
|
|
6644
|
+
if (em) lines.push(`ENTRY_FILE: ${em[1].trim()}`);
|
|
6645
|
+
if (cm) lines.push(`PREVIEW_CMD: ${cm[1].trim()}`);
|
|
6646
|
+
if (pm) lines.push(`PREVIEW_PORT: ${pm[1]}`);
|
|
6647
|
+
if (lines.length > 0) this.lastDevPreview = lines.join("\n");
|
|
6648
|
+
}
|
|
6599
6649
|
this.enqueueResult(originAgentId, { fromName, statusWord, summary: summary.slice(0, 400) });
|
|
6600
6650
|
};
|
|
6601
6651
|
}
|
|
@@ -6759,7 +6809,8 @@ ${resultLines2}`,
|
|
|
6759
6809
|
resultStatus: pending.results.every((r) => r.statusWord.includes("success")) ? "completed successfully" : "mixed results",
|
|
6760
6810
|
resultSummary: resultLines,
|
|
6761
6811
|
originalTask: originSession.originalTask ?? "",
|
|
6762
|
-
roundInfo
|
|
6812
|
+
roundInfo,
|
|
6813
|
+
devPreview: this.lastDevPreview
|
|
6763
6814
|
});
|
|
6764
6815
|
console.log(`[ResultBatch] Flushing ${pending.results.length} result(s) to ${originAgentId} (round ${this.leaderRounds}, budget=${CONFIG.delegation.budgetRounds}, ceiling=${CONFIG.delegation.hardCeilingRounds})`);
|
|
6765
6816
|
originSession.runTask(followUpTaskId, batchPrompt, void 0, teamContext);
|
|
@@ -6767,7 +6818,7 @@ ${resultLines2}`,
|
|
|
6767
6818
|
};
|
|
6768
6819
|
|
|
6769
6820
|
// ../../packages/orchestrator/src/prompt-templates.ts
|
|
6770
|
-
import { readFileSync as
|
|
6821
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync8 } from "fs";
|
|
6771
6822
|
import path7 from "path";
|
|
6772
6823
|
var PROMPT_DEFAULTS = {
|
|
6773
6824
|
"leader-initial": `You are {{name}}, the Team Lead. {{personality}}
|
|
@@ -6817,6 +6868,12 @@ Team status:
|
|
|
6817
6868
|
|
|
6818
6869
|
Delegate using: @AgentName: task description
|
|
6819
6870
|
|
|
6871
|
+
===== RULES =====
|
|
6872
|
+
- ONE task at a time. Delegate to the developer FIRST. Wait for their result before assigning Code Reviewer.
|
|
6873
|
+
- Do NOT assign Code Reviewer and Developer simultaneously \u2014 there is nothing to review until the dev is done.
|
|
6874
|
+
- Keep fixes MINIMAL. If the user reports a bug, fix THAT bug only. Do NOT add new features, tests, or process changes in the same round.
|
|
6875
|
+
- Do NOT redefine the reviewer's methodology or add new review requirements \u2014 just ask them to review the code.
|
|
6876
|
+
|
|
6820
6877
|
{{prompt}}`,
|
|
6821
6878
|
"leader-result": `You are the Team Lead. You CANNOT write or fix code. You can ONLY delegate using @Name: <task>.
|
|
6822
6879
|
|
|
@@ -6857,12 +6914,15 @@ Check WHO sent this result, then follow the matching branch:
|
|
|
6857
6914
|
\u2022 Permanent blocker (auth error, missing API key, service down) \u2192 report to the user, do not retry.
|
|
6858
6915
|
\u2022 Same error repeated twice \u2192 STOP and report to the user.
|
|
6859
6916
|
|
|
6917
|
+
===== DEVELOPER'S LAST KNOWN PREVIEW FIELDS =====
|
|
6918
|
+
{{devPreview}}
|
|
6919
|
+
|
|
6860
6920
|
===== FINAL SUMMARY FORMAT =====
|
|
6861
|
-
(Copy preview fields
|
|
6921
|
+
(Copy preview fields from DEVELOPER'S LAST KNOWN PREVIEW FIELDS above. Do NOT invent values.)
|
|
6862
6922
|
|
|
6863
|
-
ENTRY_FILE: <from
|
|
6864
|
-
PREVIEW_CMD: <from
|
|
6865
|
-
PREVIEW_PORT: <from
|
|
6923
|
+
ENTRY_FILE: <copy from above if available, otherwise OMIT>
|
|
6924
|
+
PREVIEW_CMD: <copy from above if available, otherwise OMIT. NEVER use "npm run dev" or "npm start"!>
|
|
6925
|
+
PREVIEW_PORT: <copy from above if available, otherwise OMIT>
|
|
6866
6926
|
SUMMARY: <2-3 sentence description of what was built>
|
|
6867
6927
|
|
|
6868
6928
|
RULES:
|
|
@@ -6873,95 +6933,71 @@ RULES:
|
|
|
6873
6933
|
- Do NOT include PROJECT_DIR \u2014 the system manages project directories automatically.`,
|
|
6874
6934
|
"worker-initial": `Your name is {{name}}, your role is {{role}}. {{personality}}
|
|
6875
6935
|
|
|
6876
|
-
|
|
6877
|
-
- Do the MINIMUM needed
|
|
6878
|
-
-
|
|
6879
|
-
-
|
|
6880
|
-
-
|
|
6881
|
-
|
|
6882
|
-
HARD LIMITS:
|
|
6883
|
-
- NEVER run "npm run dev", "npm start", "npx vite", "python -m http.server", or ANY command that starts a long-running server. These will hang forever and waste your budget. The system handles preview serving automatically.
|
|
6884
|
-
- Do NOT create backend servers, WebSocket servers, or any server-side code UNLESS the task explicitly requires one. Default to static HTML/CSS/JS.
|
|
6885
|
-
- You MAY install dependencies (npm install, pip install) and run ONE-SHOT build commands (npm run build, npx tsc). Never run watch/serve/dev commands.
|
|
6936
|
+
RULES:
|
|
6937
|
+
- Do the MINIMUM needed. Simple and working beats perfect.
|
|
6938
|
+
- NEVER run long-running commands (npm run dev, npm start, npx vite, live-server, python -m http.server). They hang forever and you will be killed. The system serves previews automatically.
|
|
6939
|
+
- Do NOT launch GUI apps (Pygame, Tkinter, Electron) or dev servers. You CANNOT see UI.
|
|
6940
|
+
- You MAY run one-shot commands: npm install, npm run build, npx tsc, syntax checks.
|
|
6941
|
+
- Default to static HTML/CSS/JS unless a backend is explicitly required.
|
|
6886
6942
|
{{soloHint}}
|
|
6887
6943
|
{{memory}}
|
|
6888
|
-
Start with one sentence describing your approach. Then do the work.
|
|
6889
|
-
|
|
6890
|
-
You are responsible for the COMPLETE deliverable \u2014 not just source files. This means:
|
|
6891
|
-
1. Project setup: create all config files needed (package.json, tsconfig, build config, requirements.txt, etc.)
|
|
6892
|
-
2. All source code
|
|
6893
|
-
3. Build & verify: if the project has a build step, RUN IT and fix errors until it passes
|
|
6894
|
-
4. Report how to run/preview the result (see deliverable types below)
|
|
6895
|
-
|
|
6896
|
-
VERIFICATION (MANDATORY before reporting STATUS: done):
|
|
6897
|
-
- If you created a package.json with a build script \u2192 run "npm run build" (ONE-SHOT), fix errors until it succeeds, confirm the output file exists. NEVER run "npm run dev" or "npm start" \u2014 these hang forever.
|
|
6898
|
-
- If your deliverable is an HTML file \u2192 confirm it exists and references valid scripts/styles
|
|
6899
|
-
- If your deliverable is a script (Python, Node, etc.) \u2192 run a syntax check (python -c "import ast; ast.parse(open('app.py').read())" or node --check app.js)
|
|
6900
|
-
- If NONE of the above apply \u2192 at minimum list the files and confirm the entry point exists
|
|
6901
|
-
- IMPORTANT: Do NOT launch GUI/desktop applications (Pygame, Tkinter, Electron, etc.) \u2014 they open windows that cannot be controlled. Do NOT start dev servers (vite, webpack-dev-server, live-server) \u2014 they never exit.
|
|
6902
|
-
- FINAL CHECK: confirm you can fill in at least ENTRY_FILE or PREVIEW_CMD (see deliverable types). If you cannot, your deliverable is incomplete \u2014 fix it before reporting.
|
|
6903
|
-
- Do NOT report STATUS: done unless verification passes. Fix problems yourself first.
|
|
6904
|
-
- STATUS: failed is ONLY for truly unsolvable problems (missing API keys, no network, system-level issues).
|
|
6905
6944
|
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6945
|
+
OUTPUT STYLE:
|
|
6946
|
+
- While working, output a SHORT status line (\u22648 words) at each major step, prefixed with \u2192. Example: "\u2192 Setting up project" or "\u2192 Building game logic". No other prose or narration. Do NOT write "Let me...", "I'll now...", "Looking at..." \u2014 just do the work.
|
|
6947
|
+
- After all work is done, output ONLY the structured result block below.
|
|
6909
6948
|
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6949
|
+
DELIVERABLE:
|
|
6950
|
+
- You own the COMPLETE deliverable: project setup, all source code, build & verify.
|
|
6951
|
+
- STATUS: failed is ONLY for truly unsolvable problems (missing API keys, system issues).
|
|
6913
6952
|
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6953
|
+
VERIFY BEFORE REPORTING DONE (mandatory):
|
|
6954
|
+
- If package.json has a build script \u2192 run "npm run build" (one-shot), fix errors until it passes.
|
|
6955
|
+
- If HTML deliverable \u2192 confirm the file exists and references valid scripts/styles.
|
|
6956
|
+
- If script (Python/Node) \u2192 run syntax check (node --check / python -c "import ast; ...").
|
|
6957
|
+
- FINAL CHECK: you MUST be able to fill in ENTRY_FILE or PREVIEW_CMD below. If not, your deliverable is incomplete \u2014 fix it first.
|
|
6917
6958
|
|
|
6918
|
-
|
|
6919
|
-
|
|
6959
|
+
DELIVERABLE TYPES (prefer A):
|
|
6960
|
+
A) STATIC WEB \u2192 ENTRY_FILE: index.html
|
|
6961
|
+
B) WEB SERVER (only if backend needed) \u2192 PREVIEW_CMD + PREVIEW_PORT
|
|
6962
|
+
C) DESKTOP/CLI \u2192 PREVIEW_CMD only
|
|
6920
6963
|
|
|
6921
|
-
|
|
6964
|
+
PORT RULES FOR WEB SERVERS (type B):
|
|
6965
|
+
- The system overrides your port. Your app MUST read port from the PORT environment variable.
|
|
6966
|
+
- Python: use int(os.environ.get("PORT", 5000)) \u2014 NOT a hardcoded port.
|
|
6967
|
+
- Node/JS: use process.env.PORT || 3000
|
|
6968
|
+
- Always output PREVIEW_CMD even for Vite/webpack/bundler projects (e.g. PREVIEW_CMD: npx vite).
|
|
6922
6969
|
|
|
6970
|
+
RESULT FORMAT:
|
|
6923
6971
|
STATUS: done | failed
|
|
6924
|
-
FILES_CHANGED: (
|
|
6925
|
-
ENTRY_FILE: (type A
|
|
6926
|
-
PREVIEW_CMD: (types B
|
|
6927
|
-
PREVIEW_PORT: (type B only
|
|
6928
|
-
SUMMARY: (one sentence
|
|
6929
|
-
|
|
6930
|
-
You MUST provide at least ENTRY_FILE or PREVIEW_CMD. For games and interactive projects, ENTRY_FILE is almost always correct.
|
|
6972
|
+
FILES_CHANGED: (one per line)
|
|
6973
|
+
ENTRY_FILE: (type A)
|
|
6974
|
+
PREVIEW_CMD: (types B/C only)
|
|
6975
|
+
PREVIEW_PORT: (type B only)
|
|
6976
|
+
SUMMARY: (one sentence)
|
|
6931
6977
|
|
|
6932
6978
|
{{prompt}}`,
|
|
6933
6979
|
"worker-reviewer-initial": `Your name is {{name}}, your role is {{role}}. {{personality}}
|
|
6934
6980
|
|
|
6935
|
-
|
|
6936
|
-
-
|
|
6937
|
-
-
|
|
6938
|
-
-
|
|
6939
|
-
- Do NOT add features, error handling, or improvements that were not explicitly asked for.
|
|
6940
|
-
|
|
6941
|
-
HARD LIMITS:
|
|
6942
|
-
- NEVER run "npm run dev", "npm start", "npx vite", or ANY long-running server command. These hang forever. Only use one-shot commands like "npm run build" or "node --check".
|
|
6943
|
-
- Do NOT launch GUI/desktop applications (Pygame, Tkinter, Electron, etc.) to test them \u2014 they open windows that cannot be controlled. Use syntax checks, import checks, and code reading only.
|
|
6944
|
-
|
|
6945
|
-
Code Quality (must check):
|
|
6946
|
-
- Correctness: crashes, broken logic, missing files, syntax errors.
|
|
6947
|
-
- Verify the deliverable can actually run: check that entry point exists, dependencies are declared, build output is present. For GUI/desktop apps, verify via code review and syntax checks \u2014 do NOT run them.
|
|
6948
|
-
- VERIFY WITH TOOLS, not just the developer's summary. Run "ls" to confirm reported files exist. If ENTRY_FILE is claimed, check the file is there and references valid scripts/styles. Do not trust STATUS: done at face value.
|
|
6949
|
-
- Do NOT flag security issues in prototypes \u2014 this is a demo, not production code.
|
|
6981
|
+
RULES:
|
|
6982
|
+
- NEVER run servers, dev commands, or GUI apps. You CANNOT see UI.
|
|
6983
|
+
- ONLY use: code reading, "ls" to check files, "npm run build" (one-shot), syntax checks.
|
|
6984
|
+
- This is a prototype \u2014 do NOT nitpick style, naming, formatting, or security.
|
|
6950
6985
|
|
|
6951
|
-
|
|
6952
|
-
-
|
|
6953
|
-
-
|
|
6954
|
-
- Do NOT fail for polish, extras, or stretch goals \u2014 this is a prototype. Focus on whether the main functionality works.
|
|
6986
|
+
OUTPUT STYLE:
|
|
6987
|
+
- While reviewing, output a SHORT status line (\u22648 words) at each step, prefixed with \u2192. Example: "\u2192 Checking file structure" or "\u2192 Reading game logic". No other prose.
|
|
6988
|
+
- After review, output ONLY the verdict block below.
|
|
6955
6989
|
|
|
6956
|
-
|
|
6957
|
-
|
|
6990
|
+
REVIEW CHECKLIST:
|
|
6991
|
+
1. VERIFY files exist with "ls" \u2014 do NOT trust the developer's summary at face value. Check ENTRY_FILE is real and references valid scripts/styles.
|
|
6992
|
+
2. READ the code to verify logic. Check for crashes, broken logic, missing files, syntax errors.
|
|
6993
|
+
3. Feature completeness: compare against key features in your task. Flag CORE features missing/broken as ISSUES. Ignore polish/extras.
|
|
6958
6994
|
|
|
6959
6995
|
VERDICT: PASS | FAIL
|
|
6960
|
-
- PASS =
|
|
6961
|
-
- FAIL = crashes/bugs
|
|
6962
|
-
ISSUES: (numbered list
|
|
6963
|
-
SUGGESTIONS: (optional
|
|
6964
|
-
SUMMARY: (one sentence
|
|
6996
|
+
- PASS = runs without crashes AND core features implemented
|
|
6997
|
+
- FAIL = crashes/bugs prevent usage OR core features missing
|
|
6998
|
+
ISSUES: (numbered list)
|
|
6999
|
+
SUGGESTIONS: (optional, brief)
|
|
7000
|
+
SUMMARY: (one sentence)
|
|
6965
7001
|
|
|
6966
7002
|
{{prompt}}`,
|
|
6967
7003
|
"worker-continue": `{{prompt}}`,
|
|
@@ -7028,7 +7064,7 @@ ASSIGNMENTS:
|
|
|
7028
7064
|
- Do NOT delegate. Do NOT write code. Do NOT use @AgentName: syntax outside [PLAN] tags.
|
|
7029
7065
|
|
|
7030
7066
|
If the user hasn't described their project yet, greet them and ask what they'd like to build.
|
|
7031
|
-
|
|
7067
|
+
{{memory}}
|
|
7032
7068
|
Team:
|
|
7033
7069
|
{{teamRoster}}
|
|
7034
7070
|
|
|
@@ -7113,12 +7149,12 @@ var PromptEngine = class {
|
|
|
7113
7149
|
return;
|
|
7114
7150
|
}
|
|
7115
7151
|
if (!existsSync8(this.promptsDir)) {
|
|
7116
|
-
|
|
7152
|
+
mkdirSync6(this.promptsDir, { recursive: true });
|
|
7117
7153
|
}
|
|
7118
7154
|
let written = 0;
|
|
7119
7155
|
for (const [name, content] of Object.entries(PROMPT_DEFAULTS)) {
|
|
7120
7156
|
const filePath = path7.join(this.promptsDir, `${name}.md`);
|
|
7121
|
-
|
|
7157
|
+
writeFileSync6(filePath, content, "utf-8");
|
|
7122
7158
|
written++;
|
|
7123
7159
|
}
|
|
7124
7160
|
console.log(`[Prompts] Synced ${written} default templates to ${this.promptsDir}`);
|
|
@@ -7136,7 +7172,7 @@ var PromptEngine = class {
|
|
|
7136
7172
|
const filePath = path7.join(this.promptsDir, `${name}.md`);
|
|
7137
7173
|
if (existsSync8(filePath)) {
|
|
7138
7174
|
try {
|
|
7139
|
-
merged[name] =
|
|
7175
|
+
merged[name] = readFileSync6(filePath, "utf-8");
|
|
7140
7176
|
loaded++;
|
|
7141
7177
|
} catch {
|
|
7142
7178
|
defaulted++;
|
|
@@ -7419,7 +7455,7 @@ function finalizeTeamResult(ctx) {
|
|
|
7419
7455
|
}
|
|
7420
7456
|
validateEntryFile(result, projectDir ?? workspace, workspace);
|
|
7421
7457
|
autoConstructPreviewCmd(result);
|
|
7422
|
-
if (!result.previewUrl) {
|
|
7458
|
+
if (!result.previewUrl && !result.previewPath) {
|
|
7423
7459
|
resolvePreviewUrlFromTeam(result, ctx);
|
|
7424
7460
|
}
|
|
7425
7461
|
}
|
|
@@ -7454,9 +7490,9 @@ function resolvePreviewUrlFromTeam(result, ctx) {
|
|
|
7454
7490
|
const { projectDir, workspace } = ctx;
|
|
7455
7491
|
const resolveDir = projectDir ?? workspace;
|
|
7456
7492
|
const workerPreview = ctx.detectWorkerPreview();
|
|
7457
|
-
if (workerPreview?.previewUrl) {
|
|
7458
|
-
result.previewUrl = workerPreview.previewUrl;
|
|
7459
|
-
result.previewPath = workerPreview.previewPath;
|
|
7493
|
+
if (workerPreview?.previewUrl || workerPreview?.previewPath) {
|
|
7494
|
+
if (workerPreview.previewUrl) result.previewUrl = workerPreview.previewUrl;
|
|
7495
|
+
if (workerPreview.previewPath) result.previewPath = workerPreview.previewPath;
|
|
7460
7496
|
return;
|
|
7461
7497
|
}
|
|
7462
7498
|
const allChangedFiles = result.changedFiles ?? [];
|
|
@@ -7468,19 +7504,17 @@ function resolvePreviewUrlFromTeam(result, ctx) {
|
|
|
7468
7504
|
cwd: resolveDir,
|
|
7469
7505
|
workspace
|
|
7470
7506
|
});
|
|
7471
|
-
if (preview.previewUrl)
|
|
7472
|
-
|
|
7473
|
-
result.previewPath = preview.previewPath;
|
|
7474
|
-
}
|
|
7507
|
+
if (preview.previewUrl) result.previewUrl = preview.previewUrl;
|
|
7508
|
+
if (preview.previewPath) result.previewPath = preview.previewPath;
|
|
7475
7509
|
}
|
|
7476
7510
|
|
|
7477
7511
|
// ../../packages/orchestrator/src/worktree.ts
|
|
7478
|
-
import { execSync as
|
|
7512
|
+
import { execSync as execSync3 } from "child_process";
|
|
7479
7513
|
import path9 from "path";
|
|
7480
7514
|
var TIMEOUT = 5e3;
|
|
7481
7515
|
function isGitRepo(cwd) {
|
|
7482
7516
|
try {
|
|
7483
|
-
|
|
7517
|
+
execSync3("git rev-parse --is-inside-work-tree", { cwd, stdio: "ignore", timeout: TIMEOUT });
|
|
7484
7518
|
return true;
|
|
7485
7519
|
} catch {
|
|
7486
7520
|
return false;
|
|
@@ -7493,7 +7527,7 @@ function createWorktree(workspace, agentId, taskId, agentName) {
|
|
|
7493
7527
|
const worktreePath = path9.join(worktreeDir, worktreeName);
|
|
7494
7528
|
const branch = `agent/${agentName.toLowerCase().replace(/\s+/g, "-")}/${taskId}`;
|
|
7495
7529
|
try {
|
|
7496
|
-
|
|
7530
|
+
execSync3(`git worktree add "${worktreePath}" -b "${branch}"`, {
|
|
7497
7531
|
cwd: workspace,
|
|
7498
7532
|
stdio: "pipe",
|
|
7499
7533
|
timeout: TIMEOUT
|
|
@@ -7506,30 +7540,30 @@ function createWorktree(workspace, agentId, taskId, agentName) {
|
|
|
7506
7540
|
}
|
|
7507
7541
|
function mergeWorktree(workspace, worktreePath, branch) {
|
|
7508
7542
|
try {
|
|
7509
|
-
|
|
7543
|
+
execSync3(`git merge --no-ff "${branch}"`, {
|
|
7510
7544
|
cwd: workspace,
|
|
7511
7545
|
stdio: "pipe",
|
|
7512
7546
|
timeout: TIMEOUT
|
|
7513
7547
|
});
|
|
7514
7548
|
try {
|
|
7515
|
-
|
|
7549
|
+
execSync3(`git worktree remove "${worktreePath}"`, { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
|
|
7516
7550
|
} catch {
|
|
7517
7551
|
}
|
|
7518
7552
|
try {
|
|
7519
|
-
|
|
7553
|
+
execSync3(`git branch -d "${branch}"`, { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
|
|
7520
7554
|
} catch {
|
|
7521
7555
|
}
|
|
7522
7556
|
return { success: true };
|
|
7523
7557
|
} catch (err) {
|
|
7524
7558
|
let conflictFiles = [];
|
|
7525
7559
|
try {
|
|
7526
|
-
const output =
|
|
7560
|
+
const output = execSync3("git diff --name-only --diff-filter=U", {
|
|
7527
7561
|
cwd: workspace,
|
|
7528
7562
|
encoding: "utf-8",
|
|
7529
7563
|
timeout: TIMEOUT
|
|
7530
7564
|
}).trim();
|
|
7531
7565
|
conflictFiles = output ? output.split("\n") : [];
|
|
7532
|
-
|
|
7566
|
+
execSync3("git merge --abort", { cwd: workspace, stdio: "pipe", timeout: TIMEOUT });
|
|
7533
7567
|
} catch {
|
|
7534
7568
|
}
|
|
7535
7569
|
return { success: false, conflictFiles };
|
|
@@ -7538,119 +7572,15 @@ function mergeWorktree(workspace, worktreePath, branch) {
|
|
|
7538
7572
|
function removeWorktree(worktreePath, branch, workspace) {
|
|
7539
7573
|
const cwd = workspace ?? path9.dirname(path9.dirname(worktreePath));
|
|
7540
7574
|
try {
|
|
7541
|
-
|
|
7575
|
+
execSync3(`git worktree remove --force "${worktreePath}"`, { cwd, stdio: "pipe", timeout: TIMEOUT });
|
|
7542
7576
|
} catch {
|
|
7543
7577
|
}
|
|
7544
7578
|
try {
|
|
7545
|
-
|
|
7579
|
+
execSync3(`git branch -D "${branch}"`, { cwd, stdio: "pipe", timeout: TIMEOUT });
|
|
7546
7580
|
} catch {
|
|
7547
7581
|
}
|
|
7548
7582
|
}
|
|
7549
7583
|
|
|
7550
|
-
// ../../packages/orchestrator/src/memory.ts
|
|
7551
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync9 } from "fs";
|
|
7552
|
-
import path10 from "path";
|
|
7553
|
-
import { homedir as homedir5 } from "os";
|
|
7554
|
-
var MEMORY_DIR = path10.join(homedir5(), ".bit-office", "memory");
|
|
7555
|
-
function ensureDir() {
|
|
7556
|
-
if (!existsSync9(MEMORY_DIR)) {
|
|
7557
|
-
mkdirSync6(MEMORY_DIR, { recursive: true });
|
|
7558
|
-
}
|
|
7559
|
-
}
|
|
7560
|
-
function loadStore() {
|
|
7561
|
-
const filePath = path10.join(MEMORY_DIR, "memory.json");
|
|
7562
|
-
try {
|
|
7563
|
-
if (existsSync9(filePath)) {
|
|
7564
|
-
return JSON.parse(readFileSync6(filePath, "utf-8"));
|
|
7565
|
-
}
|
|
7566
|
-
} catch {
|
|
7567
|
-
}
|
|
7568
|
-
return { reviewPatterns: [], techPreferences: [], projectHistory: [] };
|
|
7569
|
-
}
|
|
7570
|
-
function saveStore(store) {
|
|
7571
|
-
ensureDir();
|
|
7572
|
-
const filePath = path10.join(MEMORY_DIR, "memory.json");
|
|
7573
|
-
writeFileSync6(filePath, JSON.stringify(store, null, 2), "utf-8");
|
|
7574
|
-
}
|
|
7575
|
-
function recordReviewFeedback(reviewOutput) {
|
|
7576
|
-
const verdictMatch = reviewOutput.match(/VERDICT[:\s]*(\w+)/i);
|
|
7577
|
-
if (!verdictMatch || verdictMatch[1].toUpperCase() !== "FAIL") return;
|
|
7578
|
-
const issueLines = [];
|
|
7579
|
-
const issueRe = /^\s*\d+[.)]\s*(.+)/gm;
|
|
7580
|
-
let match;
|
|
7581
|
-
while ((match = issueRe.exec(reviewOutput)) !== null) {
|
|
7582
|
-
const issue = match[1].trim();
|
|
7583
|
-
if (issue.length > 10 && issue.length < 200) {
|
|
7584
|
-
issueLines.push(issue);
|
|
7585
|
-
}
|
|
7586
|
-
}
|
|
7587
|
-
if (issueLines.length === 0) return;
|
|
7588
|
-
const store = loadStore();
|
|
7589
|
-
const now = Date.now();
|
|
7590
|
-
for (const issue of issueLines) {
|
|
7591
|
-
const normalized = normalizeIssue(issue);
|
|
7592
|
-
const existing = store.reviewPatterns.find((p) => normalizeIssue(p.pattern) === normalized);
|
|
7593
|
-
if (existing) {
|
|
7594
|
-
existing.count++;
|
|
7595
|
-
existing.lastSeen = now;
|
|
7596
|
-
} else {
|
|
7597
|
-
store.reviewPatterns.push({ pattern: issue, count: 1, lastSeen: now });
|
|
7598
|
-
}
|
|
7599
|
-
}
|
|
7600
|
-
store.reviewPatterns.sort((a, b) => b.count - a.count);
|
|
7601
|
-
store.reviewPatterns = store.reviewPatterns.slice(0, 20);
|
|
7602
|
-
saveStore(store);
|
|
7603
|
-
console.log(`[Memory] Recorded ${issueLines.length} review pattern(s), total=${store.reviewPatterns.length}`);
|
|
7604
|
-
}
|
|
7605
|
-
function recordProjectCompletion(summary, tech, reviewPassed) {
|
|
7606
|
-
const store = loadStore();
|
|
7607
|
-
store.projectHistory.push({
|
|
7608
|
-
summary: summary.slice(0, 300),
|
|
7609
|
-
tech: tech.slice(0, 100),
|
|
7610
|
-
completedAt: Date.now(),
|
|
7611
|
-
reviewPassed
|
|
7612
|
-
});
|
|
7613
|
-
if (store.projectHistory.length > 50) {
|
|
7614
|
-
store.projectHistory = store.projectHistory.slice(-50);
|
|
7615
|
-
}
|
|
7616
|
-
saveStore(store);
|
|
7617
|
-
console.log(`[Memory] Recorded project completion: ${summary.slice(0, 80)}`);
|
|
7618
|
-
}
|
|
7619
|
-
function recordTechPreference(tech) {
|
|
7620
|
-
const store = loadStore();
|
|
7621
|
-
const normalized = tech.trim().toLowerCase();
|
|
7622
|
-
if (!store.techPreferences.some((t) => t.toLowerCase() === normalized)) {
|
|
7623
|
-
store.techPreferences.push(tech.trim());
|
|
7624
|
-
if (store.techPreferences.length > 10) {
|
|
7625
|
-
store.techPreferences = store.techPreferences.slice(-10);
|
|
7626
|
-
}
|
|
7627
|
-
saveStore(store);
|
|
7628
|
-
console.log(`[Memory] Recorded tech preference: ${tech}`);
|
|
7629
|
-
}
|
|
7630
|
-
}
|
|
7631
|
-
function getMemoryContext() {
|
|
7632
|
-
const store = loadStore();
|
|
7633
|
-
const sections = [];
|
|
7634
|
-
const recurring = store.reviewPatterns.filter((p) => p.count >= 2);
|
|
7635
|
-
if (recurring.length > 0) {
|
|
7636
|
-
const lines = recurring.slice(0, 5).map((p) => `- ${p.pattern} (flagged ${p.count}x)`);
|
|
7637
|
-
sections.push(`COMMON REVIEW ISSUES (avoid these):
|
|
7638
|
-
${lines.join("\n")}`);
|
|
7639
|
-
}
|
|
7640
|
-
if (store.techPreferences.length > 0) {
|
|
7641
|
-
const recent = store.techPreferences.slice(-3);
|
|
7642
|
-
sections.push(`USER'S PREFERRED TECH: ${recent.join(", ")}`);
|
|
7643
|
-
}
|
|
7644
|
-
if (sections.length === 0) return "";
|
|
7645
|
-
return `
|
|
7646
|
-
===== LEARNED FROM PREVIOUS PROJECTS =====
|
|
7647
|
-
${sections.join("\n\n")}
|
|
7648
|
-
`;
|
|
7649
|
-
}
|
|
7650
|
-
function normalizeIssue(issue) {
|
|
7651
|
-
return issue.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, " ").trim();
|
|
7652
|
-
}
|
|
7653
|
-
|
|
7654
7584
|
// ../../packages/orchestrator/src/orchestrator.ts
|
|
7655
7585
|
var Orchestrator = class extends EventEmitter {
|
|
7656
7586
|
agentManager = new AgentManager();
|
|
@@ -7705,8 +7635,8 @@ var Orchestrator = class extends EventEmitter {
|
|
|
7705
7635
|
createAgent(opts) {
|
|
7706
7636
|
const backend = this.backends.get(opts.backend ?? this.defaultBackendId) ?? this.backends.get(this.defaultBackendId);
|
|
7707
7637
|
const roleLower = opts.role.toLowerCase();
|
|
7708
|
-
const
|
|
7709
|
-
const memoryContext =
|
|
7638
|
+
const isReviewer = roleLower.includes("review");
|
|
7639
|
+
const memoryContext = !isReviewer ? getMemoryContext() : "";
|
|
7710
7640
|
const session = new AgentSession({
|
|
7711
7641
|
agentId: opts.agentId,
|
|
7712
7642
|
name: opts.name,
|
|
@@ -8210,6 +8140,152 @@ var Orchestrator = class extends EventEmitter {
|
|
|
8210
8140
|
}
|
|
8211
8141
|
};
|
|
8212
8142
|
|
|
8143
|
+
// ../../packages/orchestrator/src/preview-server.ts
|
|
8144
|
+
import { spawn as spawn2, execSync as execSync4 } from "child_process";
|
|
8145
|
+
import { existsSync as existsSync9 } from "fs";
|
|
8146
|
+
import path10 from "path";
|
|
8147
|
+
var STATIC_PORT = 9100;
|
|
8148
|
+
var COMMAND_PORT = 9101;
|
|
8149
|
+
var PreviewServer = class {
|
|
8150
|
+
process = null;
|
|
8151
|
+
currentDir = null;
|
|
8152
|
+
isDetached = false;
|
|
8153
|
+
/**
|
|
8154
|
+
* Mode 1: Serve a static file directory on a fixed port.
|
|
8155
|
+
* Returns the preview URL for the given file.
|
|
8156
|
+
*/
|
|
8157
|
+
serve(filePath) {
|
|
8158
|
+
if (!existsSync9(filePath)) {
|
|
8159
|
+
console.log(`[PreviewServer] File not found: ${filePath}`);
|
|
8160
|
+
return void 0;
|
|
8161
|
+
}
|
|
8162
|
+
const dir = path10.dirname(filePath);
|
|
8163
|
+
const fileName = path10.basename(filePath);
|
|
8164
|
+
this.stop();
|
|
8165
|
+
try {
|
|
8166
|
+
this.process = spawn2("npx", ["serve", dir, "-l", String(STATIC_PORT), "--no-clipboard"], {
|
|
8167
|
+
stdio: "ignore",
|
|
8168
|
+
detached: true
|
|
8169
|
+
});
|
|
8170
|
+
this.process.unref();
|
|
8171
|
+
this.currentDir = dir;
|
|
8172
|
+
this.isDetached = true;
|
|
8173
|
+
const url = `http://localhost:${STATIC_PORT}/${fileName}`;
|
|
8174
|
+
console.log(`[PreviewServer] Serving ${dir} on port ${STATIC_PORT}`);
|
|
8175
|
+
return url;
|
|
8176
|
+
} catch (e) {
|
|
8177
|
+
console.log(`[PreviewServer] Failed to start static serve: ${e}`);
|
|
8178
|
+
return void 0;
|
|
8179
|
+
}
|
|
8180
|
+
}
|
|
8181
|
+
/**
|
|
8182
|
+
* Mode 2: Run a command (e.g. "python app.py") and use a controlled port.
|
|
8183
|
+
* The agent-specified port is ALWAYS replaced with COMMAND_PORT to prevent
|
|
8184
|
+
* conflicts with the host system (e.g. Next.js on 3000).
|
|
8185
|
+
* Returns the preview URL.
|
|
8186
|
+
*/
|
|
8187
|
+
runCommand(cmd, cwd, agentPort) {
|
|
8188
|
+
this.stop();
|
|
8189
|
+
const port = COMMAND_PORT;
|
|
8190
|
+
cmd = cmd.replace(/\s+(?:--port|-p)\s+\d+/gi, "");
|
|
8191
|
+
if (agentPort) cmd = cmd.replace(new RegExp(`\\b${agentPort}\\b`, "g"), String(port));
|
|
8192
|
+
const isPython = /^python\b|^python3\b/i.test(cmd.trim());
|
|
8193
|
+
if (!isPython) {
|
|
8194
|
+
cmd = `${cmd} --port ${port}`;
|
|
8195
|
+
}
|
|
8196
|
+
console.log(`[PreviewServer] Command: "${cmd}" (forced port ${port})`);
|
|
8197
|
+
this.killPortHolder(port);
|
|
8198
|
+
try {
|
|
8199
|
+
this.process = spawn2(cmd, {
|
|
8200
|
+
shell: true,
|
|
8201
|
+
cwd,
|
|
8202
|
+
stdio: "ignore",
|
|
8203
|
+
detached: true,
|
|
8204
|
+
env: { ...process.env, PORT: String(port) }
|
|
8205
|
+
});
|
|
8206
|
+
this.process.unref();
|
|
8207
|
+
this.currentDir = cwd;
|
|
8208
|
+
this.isDetached = true;
|
|
8209
|
+
const url = `http://localhost:${port}`;
|
|
8210
|
+
console.log(`[PreviewServer] Running "${cmd}" in ${cwd}, preview at port ${port}`);
|
|
8211
|
+
return url;
|
|
8212
|
+
} catch (e) {
|
|
8213
|
+
console.log(`[PreviewServer] Failed to run command: ${e}`);
|
|
8214
|
+
return void 0;
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
/**
|
|
8218
|
+
* Mode 3: Launch a desktop/CLI process (no web preview URL).
|
|
8219
|
+
* Used for Pygame, Tkinter, Electron, terminal apps, etc.
|
|
8220
|
+
* NOT detached — GUI apps need the login session to access WindowServer (macOS).
|
|
8221
|
+
*/
|
|
8222
|
+
launchProcess(cmd, cwd) {
|
|
8223
|
+
this.stop();
|
|
8224
|
+
try {
|
|
8225
|
+
this.process = spawn2(cmd, {
|
|
8226
|
+
shell: true,
|
|
8227
|
+
cwd,
|
|
8228
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
8229
|
+
});
|
|
8230
|
+
this.currentDir = cwd;
|
|
8231
|
+
this.isDetached = false;
|
|
8232
|
+
console.log(`[PreviewServer] Launched "${cmd}" in ${cwd} (pid=${this.process.pid})`);
|
|
8233
|
+
this.process.stderr?.on("data", (data) => {
|
|
8234
|
+
const msg = data.toString().trim();
|
|
8235
|
+
if (msg) console.log(`[PreviewServer] stderr: ${msg.slice(0, 200)}`);
|
|
8236
|
+
});
|
|
8237
|
+
this.process.on("exit", (code) => {
|
|
8238
|
+
console.log(`[PreviewServer] Process exited with code ${code}`);
|
|
8239
|
+
});
|
|
8240
|
+
} catch (e) {
|
|
8241
|
+
console.log(`[PreviewServer] Failed to launch process: ${e}`);
|
|
8242
|
+
}
|
|
8243
|
+
}
|
|
8244
|
+
/** Kill the current process and any orphan process on managed ports */
|
|
8245
|
+
stop() {
|
|
8246
|
+
if (this.process) {
|
|
8247
|
+
try {
|
|
8248
|
+
if (this.isDetached && this.process.pid) {
|
|
8249
|
+
process.kill(-this.process.pid, "SIGTERM");
|
|
8250
|
+
} else {
|
|
8251
|
+
this.process.kill("SIGTERM");
|
|
8252
|
+
}
|
|
8253
|
+
} catch {
|
|
8254
|
+
try {
|
|
8255
|
+
this.process.kill("SIGTERM");
|
|
8256
|
+
} catch {
|
|
8257
|
+
}
|
|
8258
|
+
}
|
|
8259
|
+
this.process = null;
|
|
8260
|
+
this.currentDir = null;
|
|
8261
|
+
this.isDetached = false;
|
|
8262
|
+
console.log(`[PreviewServer] Stopped`);
|
|
8263
|
+
}
|
|
8264
|
+
this.killPortHolder(STATIC_PORT);
|
|
8265
|
+
this.killPortHolder(COMMAND_PORT);
|
|
8266
|
+
}
|
|
8267
|
+
/** Kill whatever process is listening on the given port (best-effort). */
|
|
8268
|
+
killPortHolder(port) {
|
|
8269
|
+
try {
|
|
8270
|
+
const out = execSync4(`lsof -ti :${port}`, { encoding: "utf-8", timeout: 3e3 }).trim();
|
|
8271
|
+
if (out) {
|
|
8272
|
+
for (const pid of out.split("\n")) {
|
|
8273
|
+
const n = parseInt(pid, 10);
|
|
8274
|
+
if (n > 0) {
|
|
8275
|
+
try {
|
|
8276
|
+
process.kill(n, "SIGKILL");
|
|
8277
|
+
} catch {
|
|
8278
|
+
}
|
|
8279
|
+
}
|
|
8280
|
+
}
|
|
8281
|
+
console.log(`[PreviewServer] Killed orphan process(es) on port ${port}: ${out.replace(/\n/g, ", ")}`);
|
|
8282
|
+
}
|
|
8283
|
+
} catch {
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
};
|
|
8287
|
+
var previewServer = new PreviewServer();
|
|
8288
|
+
|
|
8213
8289
|
// ../../packages/orchestrator/src/index.ts
|
|
8214
8290
|
function createOrchestrator(options) {
|
|
8215
8291
|
return new Orchestrator(options);
|
|
@@ -8799,7 +8875,25 @@ function archiveProject(agents, team) {
|
|
|
8799
8875
|
}
|
|
8800
8876
|
}
|
|
8801
8877
|
}
|
|
8878
|
+
let totalInputTokens = 0;
|
|
8879
|
+
let totalOutputTokens = 0;
|
|
8880
|
+
for (const e of projectEvents) {
|
|
8881
|
+
if (e.type === "TASK_DONE" && e.result?.tokenUsage) {
|
|
8882
|
+
totalInputTokens += e.result.tokenUsage.inputTokens ?? 0;
|
|
8883
|
+
totalOutputTokens += e.result.tokenUsage.outputTokens ?? 0;
|
|
8884
|
+
}
|
|
8885
|
+
}
|
|
8886
|
+
const tokenUsage = totalInputTokens > 0 || totalOutputTokens > 0 ? { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } : void 0;
|
|
8802
8887
|
const id = `${projectStartedAt}-${projectName || "project"}`;
|
|
8888
|
+
const filePath = path12.join(PROJECTS_DIR, `${id}.json`);
|
|
8889
|
+
let existingRatings;
|
|
8890
|
+
try {
|
|
8891
|
+
if (existsSync11(filePath)) {
|
|
8892
|
+
const existing = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
8893
|
+
existingRatings = existing.ratings;
|
|
8894
|
+
}
|
|
8895
|
+
} catch {
|
|
8896
|
+
}
|
|
8803
8897
|
const archive = {
|
|
8804
8898
|
id,
|
|
8805
8899
|
name: projectName || "Untitled Project",
|
|
@@ -8808,10 +8902,11 @@ function archiveProject(agents, team) {
|
|
|
8808
8902
|
agents,
|
|
8809
8903
|
team,
|
|
8810
8904
|
events: projectEvents,
|
|
8811
|
-
preview
|
|
8905
|
+
preview,
|
|
8906
|
+
tokenUsage,
|
|
8907
|
+
ratings: existingRatings
|
|
8812
8908
|
};
|
|
8813
8909
|
try {
|
|
8814
|
-
const filePath = path12.join(PROJECTS_DIR, `${id}.json`);
|
|
8815
8910
|
writeFileSync7(filePath, JSON.stringify(archive), "utf-8");
|
|
8816
8911
|
console.log(`[TeamState] Archived project "${archive.name}" (${projectEvents.length} events) \u2192 ${filePath}`);
|
|
8817
8912
|
return id;
|
|
@@ -8836,7 +8931,9 @@ function listProjects() {
|
|
|
8836
8931
|
endedAt: raw.endedAt,
|
|
8837
8932
|
agentNames: raw.agents.map((a) => a.name),
|
|
8838
8933
|
eventCount: raw.events.length,
|
|
8839
|
-
preview: raw.preview
|
|
8934
|
+
preview: raw.preview,
|
|
8935
|
+
tokenUsage: raw.tokenUsage,
|
|
8936
|
+
ratings: raw.ratings
|
|
8840
8937
|
});
|
|
8841
8938
|
} catch {
|
|
8842
8939
|
}
|
|
@@ -8859,6 +8956,30 @@ function loadProject(id) {
|
|
|
8859
8956
|
}
|
|
8860
8957
|
return null;
|
|
8861
8958
|
}
|
|
8959
|
+
function rateProject(ratings, projectId) {
|
|
8960
|
+
if (!existsSync11(PROJECTS_DIR)) return false;
|
|
8961
|
+
try {
|
|
8962
|
+
let filePath;
|
|
8963
|
+
if (projectId) {
|
|
8964
|
+
const safeId = projectId.replace(/[/\\]/g, "");
|
|
8965
|
+
filePath = path12.join(PROJECTS_DIR, `${safeId}.json`);
|
|
8966
|
+
if (!path12.resolve(filePath).startsWith(path12.resolve(PROJECTS_DIR))) return false;
|
|
8967
|
+
} else {
|
|
8968
|
+
const files = readdirSync2(PROJECTS_DIR).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
8969
|
+
if (files.length === 0) return false;
|
|
8970
|
+
filePath = path12.join(PROJECTS_DIR, files[0]);
|
|
8971
|
+
}
|
|
8972
|
+
if (!existsSync11(filePath)) return false;
|
|
8973
|
+
const archive = JSON.parse(readFileSync7(filePath, "utf-8"));
|
|
8974
|
+
archive.ratings = ratings;
|
|
8975
|
+
writeFileSync7(filePath, JSON.stringify(archive), "utf-8");
|
|
8976
|
+
console.log(`[TeamState] Rated project "${archive.name}":`, ratings);
|
|
8977
|
+
return true;
|
|
8978
|
+
} catch (e) {
|
|
8979
|
+
console.log(`[TeamState] Failed to rate project: ${e}`);
|
|
8980
|
+
return false;
|
|
8981
|
+
}
|
|
8982
|
+
}
|
|
8862
8983
|
|
|
8863
8984
|
// src/index.ts
|
|
8864
8985
|
registerChannel(wsChannel);
|
|
@@ -8981,6 +9102,40 @@ function saveAgentDefs(agents) {
|
|
|
8981
9102
|
}
|
|
8982
9103
|
}
|
|
8983
9104
|
var agentDefs = [];
|
|
9105
|
+
function detectDevServer(projectDir) {
|
|
9106
|
+
try {
|
|
9107
|
+
const pkgPath = path13.join(projectDir, "package.json");
|
|
9108
|
+
if (!existsSync12(pkgPath)) return null;
|
|
9109
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
9110
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
9111
|
+
if (allDeps["vite"]) return { cmd: "npx vite", port: 5173 };
|
|
9112
|
+
if (allDeps["webpack-dev-server"]) return { cmd: "npx webpack serve", port: 8080 };
|
|
9113
|
+
if (allDeps["parcel"]) return { cmd: "npx parcel index.html", port: 1234 };
|
|
9114
|
+
if (allDeps["next"]) return { cmd: "npx next dev", port: 3e3 };
|
|
9115
|
+
if (allDeps["react-scripts"]) return { cmd: "npx react-scripts start", port: 3e3 };
|
|
9116
|
+
return null;
|
|
9117
|
+
} catch {
|
|
9118
|
+
return null;
|
|
9119
|
+
}
|
|
9120
|
+
}
|
|
9121
|
+
function buildArchiveAgents() {
|
|
9122
|
+
return orc.getAllAgents().map((a) => ({
|
|
9123
|
+
agentId: a.agentId,
|
|
9124
|
+
name: a.name,
|
|
9125
|
+
role: a.role,
|
|
9126
|
+
personality: a.personality,
|
|
9127
|
+
backend: a.backend,
|
|
9128
|
+
palette: a.palette,
|
|
9129
|
+
teamId: a.teamId,
|
|
9130
|
+
isTeamLead: orc.isTeamLead(a.agentId)
|
|
9131
|
+
}));
|
|
9132
|
+
}
|
|
9133
|
+
function buildArchiveTeam() {
|
|
9134
|
+
const phases = orc.getAllTeamPhases();
|
|
9135
|
+
if (phases.length === 0) return null;
|
|
9136
|
+
const tp = phases[0];
|
|
9137
|
+
return { teamId: tp.teamId, leadAgentId: tp.leadAgentId, phase: tp.phase, projectDir: orc.getTeamProjectDir() };
|
|
9138
|
+
}
|
|
8984
9139
|
function mapOrchestratorEvent(e) {
|
|
8985
9140
|
switch (e.type) {
|
|
8986
9141
|
case "task:started":
|
|
@@ -9013,8 +9168,13 @@ function mapOrchestratorEvent(e) {
|
|
|
9013
9168
|
bufferEvent(phaseEvt);
|
|
9014
9169
|
publishEvent(phaseEvt);
|
|
9015
9170
|
persistTeamState();
|
|
9171
|
+
if (e.phase === "complete") {
|
|
9172
|
+
archiveProject(buildArchiveAgents(), buildArchiveTeam());
|
|
9173
|
+
}
|
|
9016
9174
|
return null;
|
|
9017
9175
|
}
|
|
9176
|
+
case "token:update":
|
|
9177
|
+
return { type: "TOKEN_UPDATE", agentId: e.agentId, inputTokens: e.inputTokens, outputTokens: e.outputTokens };
|
|
9018
9178
|
// New events (worktree, retry) — log only, no wire protocol equivalent yet
|
|
9019
9179
|
case "task:retrying":
|
|
9020
9180
|
console.log(`[Retry] Agent ${e.agentId} retrying task ${e.taskId} (attempt ${e.attempt}/${e.maxRetries})`);
|
|
@@ -9125,18 +9285,28 @@ ${text}]`;
|
|
|
9125
9285
|
break;
|
|
9126
9286
|
}
|
|
9127
9287
|
case "SERVE_PREVIEW": {
|
|
9128
|
-
const
|
|
9288
|
+
const cleanCmd = parsed.previewCmd?.replace(/\*\*/g, "").replace(/`/g, "").replace(/^_+|_+$/g, "").trim();
|
|
9289
|
+
const cleanPath = parsed.filePath?.replace(/\*\*/g, "").replace(/`/g, "").replace(/^_+|_+$/g, "").trim();
|
|
9290
|
+
const cmdLooksValid = cleanCmd && !/^[\[(].*[\])]$/.test(cleanCmd) && !/^none$/i.test(cleanCmd);
|
|
9129
9291
|
if (cmdLooksValid && parsed.previewPort) {
|
|
9130
9292
|
const cwd = parsed.cwd ?? config.defaultWorkspace;
|
|
9131
|
-
console.log(`[Gateway] SERVE_PREVIEW (cmd): "${
|
|
9132
|
-
previewServer.runCommand(
|
|
9293
|
+
console.log(`[Gateway] SERVE_PREVIEW (cmd): "${cleanCmd}" port=${parsed.previewPort} cwd=${cwd}`);
|
|
9294
|
+
previewServer.runCommand(cleanCmd, cwd, parsed.previewPort);
|
|
9133
9295
|
} else if (cmdLooksValid) {
|
|
9134
9296
|
const cwd = parsed.cwd ?? config.defaultWorkspace;
|
|
9135
|
-
console.log(`[Gateway] SERVE_PREVIEW (launch): "${
|
|
9136
|
-
previewServer.launchProcess(
|
|
9137
|
-
} else if (
|
|
9138
|
-
|
|
9139
|
-
|
|
9297
|
+
console.log(`[Gateway] SERVE_PREVIEW (launch): "${cleanCmd}" cwd=${cwd}`);
|
|
9298
|
+
previewServer.launchProcess(cleanCmd, cwd);
|
|
9299
|
+
} else if (cleanPath) {
|
|
9300
|
+
const projectDir = parsed.cwd ?? (cleanPath.includes("/") ? path13.dirname(cleanPath) : config.defaultWorkspace);
|
|
9301
|
+
const detected = detectDevServer(projectDir);
|
|
9302
|
+
if (detected) {
|
|
9303
|
+
console.log(`[Gateway] SERVE_PREVIEW (auto-detected ${detected.cmd}): cwd=${projectDir}`);
|
|
9304
|
+
previewServer.runCommand(detected.cmd, projectDir, detected.port);
|
|
9305
|
+
publishEvent({ type: "PREVIEW_READY", url: "http://localhost:9101" });
|
|
9306
|
+
} else {
|
|
9307
|
+
console.log(`[Gateway] SERVE_PREVIEW (static): ${cleanPath}`);
|
|
9308
|
+
previewServer.serve(cleanPath);
|
|
9309
|
+
}
|
|
9140
9310
|
}
|
|
9141
9311
|
break;
|
|
9142
9312
|
}
|
|
@@ -9261,23 +9431,7 @@ ${text}]`;
|
|
|
9261
9431
|
case "END_PROJECT": {
|
|
9262
9432
|
const agentId = parsed.agentId;
|
|
9263
9433
|
console.log(`[Gateway] END_PROJECT: agent=${agentId}`);
|
|
9264
|
-
|
|
9265
|
-
agentId: a.agentId,
|
|
9266
|
-
name: a.name,
|
|
9267
|
-
role: a.role,
|
|
9268
|
-
personality: a.personality,
|
|
9269
|
-
backend: a.backend,
|
|
9270
|
-
palette: a.palette,
|
|
9271
|
-
teamId: a.teamId,
|
|
9272
|
-
isTeamLead: orc.isTeamLead(a.agentId)
|
|
9273
|
-
}));
|
|
9274
|
-
let archiveTeam = null;
|
|
9275
|
-
const archivePhases = orc.getAllTeamPhases();
|
|
9276
|
-
if (archivePhases.length > 0) {
|
|
9277
|
-
const tp = archivePhases[0];
|
|
9278
|
-
archiveTeam = { teamId: tp.teamId, leadAgentId: tp.leadAgentId, phase: tp.phase, projectDir: orc.getTeamProjectDir() };
|
|
9279
|
-
}
|
|
9280
|
-
archiveProject(archiveAgents, archiveTeam);
|
|
9434
|
+
archiveProject(buildArchiveAgents(), buildArchiveTeam());
|
|
9281
9435
|
resetProjectBuffer();
|
|
9282
9436
|
orc.clearLeaderHistory(agentId);
|
|
9283
9437
|
if (!orc.getAgent(agentId) && parsed.name) {
|
|
@@ -9411,6 +9565,11 @@ ${text}]`;
|
|
|
9411
9565
|
});
|
|
9412
9566
|
break;
|
|
9413
9567
|
}
|
|
9568
|
+
case "RATE_PROJECT": {
|
|
9569
|
+
rateProject(parsed.ratings, parsed.projectId);
|
|
9570
|
+
recordProjectRatings(parsed.ratings);
|
|
9571
|
+
break;
|
|
9572
|
+
}
|
|
9414
9573
|
case "LIST_PROJECTS": {
|
|
9415
9574
|
const projects = listProjects();
|
|
9416
9575
|
publishEvent({ type: "PROJECT_LIST", projects });
|
|
@@ -9541,6 +9700,7 @@ async function main() {
|
|
|
9541
9700
|
orc.on("task:queued", forwardEvent);
|
|
9542
9701
|
orc.on("worktree:created", forwardEvent);
|
|
9543
9702
|
orc.on("worktree:merged", forwardEvent);
|
|
9703
|
+
orc.on("token:update", forwardEvent);
|
|
9544
9704
|
orc.on("agent:created", forwardEvent);
|
|
9545
9705
|
orc.on("agent:fired", forwardEvent);
|
|
9546
9706
|
orc.on("task:result-returned", forwardEvent);
|