bit-office 1.2.1 → 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 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
  ]);
@@ -4392,7 +4398,8 @@ var ProjectListEvent = external_exports.object({
4392
4398
  agentNames: external_exports.array(external_exports.string()),
4393
4399
  eventCount: external_exports.number(),
4394
4400
  preview: ProjectPreviewSchema,
4395
- tokenUsage: external_exports.object({ inputTokens: external_exports.number(), outputTokens: external_exports.number() }).optional()
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()
4396
4403
  }))
4397
4404
  });
4398
4405
  var ProjectDataEvent = external_exports.object({
@@ -4403,6 +4410,10 @@ var ProjectDataEvent = external_exports.object({
4403
4410
  endedAt: external_exports.number(),
4404
4411
  events: external_exports.array(external_exports.any())
4405
4412
  });
4413
+ var PreviewReadyEvent = external_exports.object({
4414
+ type: external_exports.literal("PREVIEW_READY"),
4415
+ url: external_exports.string()
4416
+ });
4406
4417
  var GatewayEventSchema = external_exports.discriminatedUnion("type", [
4407
4418
  AgentsSyncEvent,
4408
4419
  AgentStatusEvent,
@@ -4422,7 +4433,8 @@ var GatewayEventSchema = external_exports.discriminatedUnion("type", [
4422
4433
  AgentDefsEvent,
4423
4434
  SuggestionEvent,
4424
4435
  ProjectListEvent,
4425
- ProjectDataEvent
4436
+ ProjectDataEvent,
4437
+ PreviewReadyEvent
4426
4438
  ]);
4427
4439
 
4428
4440
  // ../../packages/shared/src/presets.ts
@@ -5337,9 +5349,9 @@ var CONFIG = {
5337
5349
 
5338
5350
  // ../../packages/orchestrator/src/agent-session.ts
5339
5351
  import { spawn, execSync as execSync2 } from "child_process";
5340
- import path4 from "path";
5341
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
5342
- import { homedir as homedir4 } from "os";
5352
+ import path5 from "path";
5353
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
5354
+ import { homedir as homedir5 } from "os";
5343
5355
 
5344
5356
  // ../../packages/orchestrator/src/preview-resolver.ts
5345
5357
  import { existsSync as existsSync5 } from "fs";
@@ -5413,19 +5425,23 @@ function parseAgentOutput(raw, fallbackText) {
5413
5425
  const entryFileMatch = text.match(/ENTRY_FILE:\s*(.+)/i);
5414
5426
  const projectDirMatch = text.match(/PROJECT_DIR:\s*(.+)/i);
5415
5427
  const previewCmdMatch = text.match(/PREVIEW_CMD:\s*(.+)/i);
5416
- 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();
5417
5430
  const changedFiles = [];
5418
5431
  if (filesMatch) {
5419
5432
  const fileList = filesMatch[1].trim();
5420
5433
  for (const f of fileList.split(/[,\n]+/)) {
5421
- const cleaned = f.trim().replace(/^[-*]\s*/, "");
5434
+ const cleaned = stripMarkdown(f.trim().replace(/^[-*]\s*/, ""));
5422
5435
  if (cleaned) changedFiles.push(cleaned);
5423
5436
  }
5424
5437
  }
5425
5438
  const isPlaceholder = (v) => !v || /^[\[(].*not provided.*[\])]$/i.test(v) || /^[\[(].*n\/?a.*[\])]$/i.test(v) || /^none$/i.test(v);
5426
- const entryFile = isPlaceholder(entryFileMatch?.[1]?.trim()) ? void 0 : entryFileMatch[1].trim();
5427
- const projectDir = isPlaceholder(projectDirMatch?.[1]?.trim()) ? void 0 : projectDirMatch[1].trim();
5428
- const previewCmd = isPlaceholder(previewCmdMatch?.[1]?.trim()) ? void 0 : previewCmdMatch[1].trim();
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);
5429
5445
  const previewPort = previewPortMatch ? parseInt(previewPortMatch[1], 10) : void 0;
5430
5446
  if (summaryMatch) {
5431
5447
  return { summary: summaryMatch[1].trim(), fullOutput, changedFiles, entryFile, projectDir, previewCmd, previewPort };
@@ -5465,10 +5481,139 @@ function extractFallbackSummary(raw, _hasFiles, _entryFile, _projectDir) {
5465
5481
 
5466
5482
  // ../../packages/orchestrator/src/agent-session.ts
5467
5483
  import { nanoid as nanoid3 } from "nanoid";
5468
- var SESSION_FILE = path4.join(homedir4(), ".bit-office", "agent-sessions.json");
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");
5469
5614
  function loadSessionMap() {
5470
5615
  try {
5471
- if (existsSync6(SESSION_FILE)) return JSON.parse(readFileSync4(SESSION_FILE, "utf-8"));
5616
+ if (existsSync7(SESSION_FILE)) return JSON.parse(readFileSync5(SESSION_FILE, "utf-8"));
5472
5617
  } catch {
5473
5618
  }
5474
5619
  return {};
@@ -5477,15 +5622,15 @@ function clearSessionId(agentId) {
5477
5622
  saveSessionId(agentId, null);
5478
5623
  }
5479
5624
  function saveSessionId(agentId, sessionId) {
5480
- const dir = path4.dirname(SESSION_FILE);
5481
- if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
5625
+ const dir = path5.dirname(SESSION_FILE);
5626
+ if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
5482
5627
  const map = loadSessionMap();
5483
5628
  if (sessionId) {
5484
5629
  map[agentId] = sessionId;
5485
5630
  } else {
5486
5631
  delete map[agentId];
5487
5632
  }
5488
- writeFileSync4(SESSION_FILE, JSON.stringify(map), "utf-8");
5633
+ writeFileSync5(SESSION_FILE, JSON.stringify(map), "utf-8");
5489
5634
  }
5490
5635
  var AgentSession = class {
5491
5636
  agentId;
@@ -5639,7 +5784,7 @@ var AgentSession = class {
5639
5784
  teamRoster: teamContext ?? "",
5640
5785
  originalTask,
5641
5786
  prompt,
5642
- memory: this._memoryContext,
5787
+ memory: this._memoryContext || getMemoryContext(),
5643
5788
  soloHint: this.teamId ? "" : `- You are a SOLO developer. Do NOT delegate, assign tasks, or mention other team members. Do ALL the work yourself.
5644
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.`
5645
5790
  };
@@ -5969,7 +6114,7 @@ var AgentSession = class {
5969
6114
  detectPreview() {
5970
6115
  const result = this.extractResult();
5971
6116
  const baseCwd = this.currentCwd ?? this.workspace;
5972
- const cwd = result.projectDir ? path4.isAbsolute(result.projectDir) ? result.projectDir : path4.join(baseCwd, result.projectDir) : baseCwd;
6117
+ const cwd = result.projectDir ? path5.isAbsolute(result.projectDir) ? result.projectDir : path5.join(baseCwd, result.projectDir) : baseCwd;
5973
6118
  return resolvePreview({
5974
6119
  entryFile: result.entryFile,
5975
6120
  previewCmd: result.previewCmd,
@@ -6184,7 +6329,7 @@ var AgentManager = class {
6184
6329
 
6185
6330
  // ../../packages/orchestrator/src/delegation.ts
6186
6331
  import { nanoid as nanoid4 } from "nanoid";
6187
- import path5 from "path";
6332
+ import path6 from "path";
6188
6333
  var DelegationRouter = class {
6189
6334
  /** All per-task delegation metadata, keyed by taskId */
6190
6335
  tasks = /* @__PURE__ */ new Map();
@@ -6206,6 +6351,8 @@ var DelegationRouter = class {
6206
6351
  devFixAttempts = /* @__PURE__ */ new Map();
6207
6352
  /** Tracks which dev agent was last assigned to work (for reviewer → dev routing) */
6208
6353
  lastDevAgentId = null;
6354
+ /** Last known preview fields from developer output (survives across rounds for leader context) */
6355
+ lastDevPreview = "";
6209
6356
  agentManager;
6210
6357
  promptEngine;
6211
6358
  emitEvent;
@@ -6309,6 +6456,7 @@ var DelegationRouter = class {
6309
6456
  this.teamProjectDir = null;
6310
6457
  this.devFixAttempts.clear();
6311
6458
  this.lastDevAgentId = null;
6459
+ this.lastDevPreview = "";
6312
6460
  for (const pending of this.pendingResults.values()) {
6313
6461
  clearTimeout(pending.timer);
6314
6462
  }
@@ -6371,7 +6519,7 @@ var DelegationRouter = class {
6371
6519
  const dirPart = dirMatch[1].replace(/\/$/, "");
6372
6520
  const leaderSession = this.agentManager.get(fromAgentId);
6373
6521
  if (leaderSession) {
6374
- repoPath = path5.resolve(leaderSession.workspaceDir, dirPart);
6522
+ repoPath = path6.resolve(leaderSession.workspaceDir, dirPart);
6375
6523
  }
6376
6524
  }
6377
6525
  }
@@ -6487,6 +6635,17 @@ SUMMARY: (one sentence overall assessment)`
6487
6635
  if (this.tryDirectFix(agentId, fromSession, fullOutput ?? summary, originAgentId)) {
6488
6636
  return;
6489
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
+ }
6490
6649
  this.enqueueResult(originAgentId, { fromName, statusWord, summary: summary.slice(0, 400) });
6491
6650
  };
6492
6651
  }
@@ -6650,7 +6809,8 @@ ${resultLines2}`,
6650
6809
  resultStatus: pending.results.every((r) => r.statusWord.includes("success")) ? "completed successfully" : "mixed results",
6651
6810
  resultSummary: resultLines,
6652
6811
  originalTask: originSession.originalTask ?? "",
6653
- roundInfo
6812
+ roundInfo,
6813
+ devPreview: this.lastDevPreview
6654
6814
  });
6655
6815
  console.log(`[ResultBatch] Flushing ${pending.results.length} result(s) to ${originAgentId} (round ${this.leaderRounds}, budget=${CONFIG.delegation.budgetRounds}, ceiling=${CONFIG.delegation.hardCeilingRounds})`);
6656
6816
  originSession.runTask(followUpTaskId, batchPrompt, void 0, teamContext);
@@ -6658,8 +6818,8 @@ ${resultLines2}`,
6658
6818
  };
6659
6819
 
6660
6820
  // ../../packages/orchestrator/src/prompt-templates.ts
6661
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
6662
- import path6 from "path";
6821
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync8 } from "fs";
6822
+ import path7 from "path";
6663
6823
  var PROMPT_DEFAULTS = {
6664
6824
  "leader-initial": `You are {{name}}, the Team Lead. {{personality}}
6665
6825
  You CANNOT write code, run commands, or use any tools. You can ONLY delegate.
@@ -6708,6 +6868,12 @@ Team status:
6708
6868
 
6709
6869
  Delegate using: @AgentName: task description
6710
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
+
6711
6877
  {{prompt}}`,
6712
6878
  "leader-result": `You are the Team Lead. You CANNOT write or fix code. You can ONLY delegate using @Name: <task>.
6713
6879
 
@@ -6748,12 +6914,15 @@ Check WHO sent this result, then follow the matching branch:
6748
6914
  \u2022 Permanent blocker (auth error, missing API key, service down) \u2192 report to the user, do not retry.
6749
6915
  \u2022 Same error repeated twice \u2192 STOP and report to the user.
6750
6916
 
6917
+ ===== DEVELOPER'S LAST KNOWN PREVIEW FIELDS =====
6918
+ {{devPreview}}
6919
+
6751
6920
  ===== FINAL SUMMARY FORMAT =====
6752
- (Copy preview fields EXACTLY from the developer's LAST successful report. Only include fields the dev actually provided \u2014 do NOT invent values.)
6921
+ (Copy preview fields from DEVELOPER'S LAST KNOWN PREVIEW FIELDS above. Do NOT invent values.)
6753
6922
 
6754
- ENTRY_FILE: <from dev \u2014 e.g. index.html, dist/index.html. OMIT if dev didn't provide one>
6755
- PREVIEW_CMD: <from dev \u2014 e.g. "python app.py". OMIT if dev didn't provide one. NEVER use "npm run dev" or "npm start"!>
6756
- PREVIEW_PORT: <from dev \u2014 e.g. 5000. OMIT if dev didn't provide one>
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>
6757
6926
  SUMMARY: <2-3 sentence description of what was built>
6758
6927
 
6759
6928
  RULES:
@@ -6792,6 +6961,12 @@ A) STATIC WEB \u2192 ENTRY_FILE: index.html
6792
6961
  B) WEB SERVER (only if backend needed) \u2192 PREVIEW_CMD + PREVIEW_PORT
6793
6962
  C) DESKTOP/CLI \u2192 PREVIEW_CMD only
6794
6963
 
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).
6969
+
6795
6970
  RESULT FORMAT:
6796
6971
  STATUS: done | failed
6797
6972
  FILES_CHANGED: (one per line)
@@ -6889,7 +7064,7 @@ ASSIGNMENTS:
6889
7064
  - Do NOT delegate. Do NOT write code. Do NOT use @AgentName: syntax outside [PLAN] tags.
6890
7065
 
6891
7066
  If the user hasn't described their project yet, greet them and ask what they'd like to build.
6892
-
7067
+ {{memory}}
6893
7068
  Team:
6894
7069
  {{teamRoster}}
6895
7070
 
@@ -6973,13 +7148,13 @@ var PromptEngine = class {
6973
7148
  console.log(`[Prompts] No promptsDir configured, using ${Object.keys(PROMPT_DEFAULTS).length} default templates`);
6974
7149
  return;
6975
7150
  }
6976
- if (!existsSync7(this.promptsDir)) {
6977
- mkdirSync5(this.promptsDir, { recursive: true });
7151
+ if (!existsSync8(this.promptsDir)) {
7152
+ mkdirSync6(this.promptsDir, { recursive: true });
6978
7153
  }
6979
7154
  let written = 0;
6980
7155
  for (const [name, content] of Object.entries(PROMPT_DEFAULTS)) {
6981
- const filePath = path6.join(this.promptsDir, `${name}.md`);
6982
- writeFileSync5(filePath, content, "utf-8");
7156
+ const filePath = path7.join(this.promptsDir, `${name}.md`);
7157
+ writeFileSync6(filePath, content, "utf-8");
6983
7158
  written++;
6984
7159
  }
6985
7160
  console.log(`[Prompts] Synced ${written} default templates to ${this.promptsDir}`);
@@ -6994,10 +7169,10 @@ var PromptEngine = class {
6994
7169
  let defaulted = 0;
6995
7170
  if (this.promptsDir) {
6996
7171
  for (const name of Object.keys(PROMPT_DEFAULTS)) {
6997
- const filePath = path6.join(this.promptsDir, `${name}.md`);
6998
- if (existsSync7(filePath)) {
7172
+ const filePath = path7.join(this.promptsDir, `${name}.md`);
7173
+ if (existsSync8(filePath)) {
6999
7174
  try {
7000
- merged[name] = readFileSync5(filePath, "utf-8");
7175
+ merged[name] = readFileSync6(filePath, "utf-8");
7001
7176
  loaded++;
7002
7177
  } catch {
7003
7178
  defaulted++;
@@ -7258,7 +7433,7 @@ var PhaseMachine = class {
7258
7433
  };
7259
7434
 
7260
7435
  // ../../packages/orchestrator/src/result-finalizer.ts
7261
- import path7 from "path";
7436
+ import path8 from "path";
7262
7437
  function finalizeTeamResult(ctx) {
7263
7438
  const { result, teamPreview, teamChangedFiles, projectDir, workspace } = ctx;
7264
7439
  if (teamChangedFiles.size > 0) {
@@ -7288,12 +7463,12 @@ function validateEntryFile(result, projectDir, workspace) {
7288
7463
  if (!result.entryFile) return;
7289
7464
  const resolved = resolveAgentPath(result.entryFile, projectDir, workspace);
7290
7465
  if (resolved) {
7291
- result.entryFile = path7.relative(projectDir, resolved);
7466
+ result.entryFile = path8.relative(projectDir, resolved);
7292
7467
  return;
7293
7468
  }
7294
7469
  const allFiles = result.changedFiles ?? [];
7295
- const ext = path7.extname(result.entryFile).toLowerCase();
7296
- const candidate = allFiles.map((f) => path7.basename(f)).find((f) => path7.extname(f).toLowerCase() === ext);
7470
+ const ext = path8.extname(result.entryFile).toLowerCase();
7471
+ const candidate = allFiles.map((f) => path8.basename(f)).find((f) => path8.extname(f).toLowerCase() === ext);
7297
7472
  if (candidate) {
7298
7473
  console.log(`[ResultFinalizer] entryFile "${result.entryFile}" not found, using "${candidate}" from changedFiles`);
7299
7474
  result.entryFile = candidate;
@@ -7304,7 +7479,7 @@ function validateEntryFile(result, projectDir, workspace) {
7304
7479
  }
7305
7480
  function autoConstructPreviewCmd(result) {
7306
7481
  if (!result.entryFile || result.previewCmd || /\.html?$/i.test(result.entryFile)) return;
7307
- const ext = path7.extname(result.entryFile).toLowerCase();
7482
+ const ext = path8.extname(result.entryFile).toLowerCase();
7308
7483
  const runner = CONFIG.preview.runners[ext];
7309
7484
  if (runner) {
7310
7485
  result.previewCmd = `${runner} ${result.entryFile}`;
@@ -7335,7 +7510,7 @@ function resolvePreviewUrlFromTeam(result, ctx) {
7335
7510
 
7336
7511
  // ../../packages/orchestrator/src/worktree.ts
7337
7512
  import { execSync as execSync3 } from "child_process";
7338
- import path8 from "path";
7513
+ import path9 from "path";
7339
7514
  var TIMEOUT = 5e3;
7340
7515
  function isGitRepo(cwd) {
7341
7516
  try {
@@ -7347,9 +7522,9 @@ function isGitRepo(cwd) {
7347
7522
  }
7348
7523
  function createWorktree(workspace, agentId, taskId, agentName) {
7349
7524
  if (!isGitRepo(workspace)) return null;
7350
- const worktreeDir = path8.join(workspace, ".worktrees");
7525
+ const worktreeDir = path9.join(workspace, ".worktrees");
7351
7526
  const worktreeName = `${agentId}-${taskId}`;
7352
- const worktreePath = path8.join(worktreeDir, worktreeName);
7527
+ const worktreePath = path9.join(worktreeDir, worktreeName);
7353
7528
  const branch = `agent/${agentName.toLowerCase().replace(/\s+/g, "-")}/${taskId}`;
7354
7529
  try {
7355
7530
  execSync3(`git worktree add "${worktreePath}" -b "${branch}"`, {
@@ -7395,7 +7570,7 @@ function mergeWorktree(workspace, worktreePath, branch) {
7395
7570
  }
7396
7571
  }
7397
7572
  function removeWorktree(worktreePath, branch, workspace) {
7398
- const cwd = workspace ?? path8.dirname(path8.dirname(worktreePath));
7573
+ const cwd = workspace ?? path9.dirname(path9.dirname(worktreePath));
7399
7574
  try {
7400
7575
  execSync3(`git worktree remove --force "${worktreePath}"`, { cwd, stdio: "pipe", timeout: TIMEOUT });
7401
7576
  } catch {
@@ -7406,110 +7581,6 @@ function removeWorktree(worktreePath, branch, workspace) {
7406
7581
  }
7407
7582
  }
7408
7583
 
7409
- // ../../packages/orchestrator/src/memory.ts
7410
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync8 } from "fs";
7411
- import path9 from "path";
7412
- import { homedir as homedir5 } from "os";
7413
- var MEMORY_DIR = path9.join(homedir5(), ".bit-office", "memory");
7414
- function ensureDir() {
7415
- if (!existsSync8(MEMORY_DIR)) {
7416
- mkdirSync6(MEMORY_DIR, { recursive: true });
7417
- }
7418
- }
7419
- function loadStore() {
7420
- const filePath = path9.join(MEMORY_DIR, "memory.json");
7421
- try {
7422
- if (existsSync8(filePath)) {
7423
- return JSON.parse(readFileSync6(filePath, "utf-8"));
7424
- }
7425
- } catch {
7426
- }
7427
- return { reviewPatterns: [], techPreferences: [], projectHistory: [] };
7428
- }
7429
- function saveStore(store) {
7430
- ensureDir();
7431
- const filePath = path9.join(MEMORY_DIR, "memory.json");
7432
- writeFileSync6(filePath, JSON.stringify(store, null, 2), "utf-8");
7433
- }
7434
- function recordReviewFeedback(reviewOutput) {
7435
- const verdictMatch = reviewOutput.match(/VERDICT[:\s]*(\w+)/i);
7436
- if (!verdictMatch || verdictMatch[1].toUpperCase() !== "FAIL") return;
7437
- const issueLines = [];
7438
- const issueRe = /^\s*\d+[.)]\s*(.+)/gm;
7439
- let match;
7440
- while ((match = issueRe.exec(reviewOutput)) !== null) {
7441
- const issue = match[1].trim();
7442
- if (issue.length > 10 && issue.length < 200) {
7443
- issueLines.push(issue);
7444
- }
7445
- }
7446
- if (issueLines.length === 0) return;
7447
- const store = loadStore();
7448
- const now = Date.now();
7449
- for (const issue of issueLines) {
7450
- const normalized = normalizeIssue(issue);
7451
- const existing = store.reviewPatterns.find((p) => normalizeIssue(p.pattern) === normalized);
7452
- if (existing) {
7453
- existing.count++;
7454
- existing.lastSeen = now;
7455
- } else {
7456
- store.reviewPatterns.push({ pattern: issue, count: 1, lastSeen: now });
7457
- }
7458
- }
7459
- store.reviewPatterns.sort((a, b) => b.count - a.count);
7460
- store.reviewPatterns = store.reviewPatterns.slice(0, 20);
7461
- saveStore(store);
7462
- console.log(`[Memory] Recorded ${issueLines.length} review pattern(s), total=${store.reviewPatterns.length}`);
7463
- }
7464
- function recordProjectCompletion(summary, tech, reviewPassed) {
7465
- const store = loadStore();
7466
- store.projectHistory.push({
7467
- summary: summary.slice(0, 300),
7468
- tech: tech.slice(0, 100),
7469
- completedAt: Date.now(),
7470
- reviewPassed
7471
- });
7472
- if (store.projectHistory.length > 50) {
7473
- store.projectHistory = store.projectHistory.slice(-50);
7474
- }
7475
- saveStore(store);
7476
- console.log(`[Memory] Recorded project completion: ${summary.slice(0, 80)}`);
7477
- }
7478
- function recordTechPreference(tech) {
7479
- const store = loadStore();
7480
- const normalized = tech.trim().toLowerCase();
7481
- if (!store.techPreferences.some((t) => t.toLowerCase() === normalized)) {
7482
- store.techPreferences.push(tech.trim());
7483
- if (store.techPreferences.length > 10) {
7484
- store.techPreferences = store.techPreferences.slice(-10);
7485
- }
7486
- saveStore(store);
7487
- console.log(`[Memory] Recorded tech preference: ${tech}`);
7488
- }
7489
- }
7490
- function getMemoryContext() {
7491
- const store = loadStore();
7492
- const sections = [];
7493
- const recurring = store.reviewPatterns.filter((p) => p.count >= 2);
7494
- if (recurring.length > 0) {
7495
- const lines = recurring.slice(0, 5).map((p) => `- ${p.pattern} (flagged ${p.count}x)`);
7496
- sections.push(`COMMON REVIEW ISSUES (avoid these):
7497
- ${lines.join("\n")}`);
7498
- }
7499
- if (store.techPreferences.length > 0) {
7500
- const recent = store.techPreferences.slice(-3);
7501
- sections.push(`USER'S PREFERRED TECH: ${recent.join(", ")}`);
7502
- }
7503
- if (sections.length === 0) return "";
7504
- return `
7505
- ===== LEARNED FROM PREVIOUS PROJECTS =====
7506
- ${sections.join("\n\n")}
7507
- `;
7508
- }
7509
- function normalizeIssue(issue) {
7510
- return issue.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, " ").trim();
7511
- }
7512
-
7513
7584
  // ../../packages/orchestrator/src/orchestrator.ts
7514
7585
  var Orchestrator = class extends EventEmitter {
7515
7586
  agentManager = new AgentManager();
@@ -7564,8 +7635,8 @@ var Orchestrator = class extends EventEmitter {
7564
7635
  createAgent(opts) {
7565
7636
  const backend = this.backends.get(opts.backend ?? this.defaultBackendId) ?? this.backends.get(this.defaultBackendId);
7566
7637
  const roleLower = opts.role.toLowerCase();
7567
- const isDevWorker = !roleLower.includes("review") && !roleLower.includes("lead");
7568
- const memoryContext = isDevWorker ? getMemoryContext() : "";
7638
+ const isReviewer = roleLower.includes("review");
7639
+ const memoryContext = !isReviewer ? getMemoryContext() : "";
7569
7640
  const session = new AgentSession({
7570
7641
  agentId: opts.agentId,
7571
7642
  name: opts.name,
@@ -8118,7 +8189,10 @@ var PreviewServer = class {
8118
8189
  const port = COMMAND_PORT;
8119
8190
  cmd = cmd.replace(/\s+(?:--port|-p)\s+\d+/gi, "");
8120
8191
  if (agentPort) cmd = cmd.replace(new RegExp(`\\b${agentPort}\\b`, "g"), String(port));
8121
- cmd = `${cmd} --port ${port}`;
8192
+ const isPython = /^python\b|^python3\b/i.test(cmd.trim());
8193
+ if (!isPython) {
8194
+ cmd = `${cmd} --port ${port}`;
8195
+ }
8122
8196
  console.log(`[PreviewServer] Command: "${cmd}" (forced port ${port})`);
8123
8197
  this.killPortHolder(port);
8124
8198
  try {
@@ -8811,6 +8885,15 @@ function archiveProject(agents, team) {
8811
8885
  }
8812
8886
  const tokenUsage = totalInputTokens > 0 || totalOutputTokens > 0 ? { inputTokens: totalInputTokens, outputTokens: totalOutputTokens } : void 0;
8813
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
+ }
8814
8897
  const archive = {
8815
8898
  id,
8816
8899
  name: projectName || "Untitled Project",
@@ -8820,10 +8903,10 @@ function archiveProject(agents, team) {
8820
8903
  team,
8821
8904
  events: projectEvents,
8822
8905
  preview,
8823
- tokenUsage
8906
+ tokenUsage,
8907
+ ratings: existingRatings
8824
8908
  };
8825
8909
  try {
8826
- const filePath = path12.join(PROJECTS_DIR, `${id}.json`);
8827
8910
  writeFileSync7(filePath, JSON.stringify(archive), "utf-8");
8828
8911
  console.log(`[TeamState] Archived project "${archive.name}" (${projectEvents.length} events) \u2192 ${filePath}`);
8829
8912
  return id;
@@ -8849,7 +8932,8 @@ function listProjects() {
8849
8932
  agentNames: raw.agents.map((a) => a.name),
8850
8933
  eventCount: raw.events.length,
8851
8934
  preview: raw.preview,
8852
- tokenUsage: raw.tokenUsage
8935
+ tokenUsage: raw.tokenUsage,
8936
+ ratings: raw.ratings
8853
8937
  });
8854
8938
  } catch {
8855
8939
  }
@@ -8872,6 +8956,30 @@ function loadProject(id) {
8872
8956
  }
8873
8957
  return null;
8874
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
+ }
8875
8983
 
8876
8984
  // src/index.ts
8877
8985
  registerChannel(wsChannel);
@@ -8994,6 +9102,40 @@ function saveAgentDefs(agents) {
8994
9102
  }
8995
9103
  }
8996
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
+ }
8997
9139
  function mapOrchestratorEvent(e) {
8998
9140
  switch (e.type) {
8999
9141
  case "task:started":
@@ -9026,6 +9168,9 @@ function mapOrchestratorEvent(e) {
9026
9168
  bufferEvent(phaseEvt);
9027
9169
  publishEvent(phaseEvt);
9028
9170
  persistTeamState();
9171
+ if (e.phase === "complete") {
9172
+ archiveProject(buildArchiveAgents(), buildArchiveTeam());
9173
+ }
9029
9174
  return null;
9030
9175
  }
9031
9176
  case "token:update":
@@ -9140,18 +9285,28 @@ ${text}]`;
9140
9285
  break;
9141
9286
  }
9142
9287
  case "SERVE_PREVIEW": {
9143
- const cmdLooksValid = parsed.previewCmd && !/^[\[(].*[\])]$/.test(parsed.previewCmd) && !/^none$/i.test(parsed.previewCmd);
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);
9144
9291
  if (cmdLooksValid && parsed.previewPort) {
9145
9292
  const cwd = parsed.cwd ?? config.defaultWorkspace;
9146
- console.log(`[Gateway] SERVE_PREVIEW (cmd): "${parsed.previewCmd}" port=${parsed.previewPort} cwd=${cwd}`);
9147
- previewServer.runCommand(parsed.previewCmd, cwd, parsed.previewPort);
9293
+ console.log(`[Gateway] SERVE_PREVIEW (cmd): "${cleanCmd}" port=${parsed.previewPort} cwd=${cwd}`);
9294
+ previewServer.runCommand(cleanCmd, cwd, parsed.previewPort);
9148
9295
  } else if (cmdLooksValid) {
9149
9296
  const cwd = parsed.cwd ?? config.defaultWorkspace;
9150
- console.log(`[Gateway] SERVE_PREVIEW (launch): "${parsed.previewCmd}" cwd=${cwd}`);
9151
- previewServer.launchProcess(parsed.previewCmd, cwd);
9152
- } else if (parsed.filePath) {
9153
- console.log(`[Gateway] SERVE_PREVIEW (static): ${parsed.filePath}`);
9154
- previewServer.serve(parsed.filePath);
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
+ }
9155
9310
  }
9156
9311
  break;
9157
9312
  }
@@ -9276,23 +9431,7 @@ ${text}]`;
9276
9431
  case "END_PROJECT": {
9277
9432
  const agentId = parsed.agentId;
9278
9433
  console.log(`[Gateway] END_PROJECT: agent=${agentId}`);
9279
- const archiveAgents = orc.getAllAgents().map((a) => ({
9280
- agentId: a.agentId,
9281
- name: a.name,
9282
- role: a.role,
9283
- personality: a.personality,
9284
- backend: a.backend,
9285
- palette: a.palette,
9286
- teamId: a.teamId,
9287
- isTeamLead: orc.isTeamLead(a.agentId)
9288
- }));
9289
- let archiveTeam = null;
9290
- const archivePhases = orc.getAllTeamPhases();
9291
- if (archivePhases.length > 0) {
9292
- const tp = archivePhases[0];
9293
- archiveTeam = { teamId: tp.teamId, leadAgentId: tp.leadAgentId, phase: tp.phase, projectDir: orc.getTeamProjectDir() };
9294
- }
9295
- archiveProject(archiveAgents, archiveTeam);
9434
+ archiveProject(buildArchiveAgents(), buildArchiveTeam());
9296
9435
  resetProjectBuffer();
9297
9436
  orc.clearLeaderHistory(agentId);
9298
9437
  if (!orc.getAgent(agentId) && parsed.name) {
@@ -9426,6 +9565,11 @@ ${text}]`;
9426
9565
  });
9427
9566
  break;
9428
9567
  }
9568
+ case "RATE_PROJECT": {
9569
+ rateProject(parsed.ratings, parsed.projectId);
9570
+ recordProjectRatings(parsed.ratings);
9571
+ break;
9572
+ }
9429
9573
  case "LIST_PROJECTS": {
9430
9574
  const projects = listProjects();
9431
9575
  publishEvent({ type: "PROJECT_LIST", projects });