flavor-code 1.3.0 → 1.3.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/README.md CHANGED
@@ -204,7 +204,9 @@ npm run desktop:pack # Windows portable directory
204
204
  npm run desktop:dist # Windows NSIS installer
205
205
  ```
206
206
 
207
- The desktop app provides project and session switching, streaming Markdown, tool and diff views, permission confirmations, task status, and management of Skills, MCP, memory, and models.
207
+ The desktop app keeps multiple projects open and can run up to four independent tasks concurrently inside one project; switching projects or tasks does not stop background work. Completion, failure, attention, and interruption events enter a persistent activity inbox and trigger native notifications, while unread completions retain a blue dot. Projects can be pinned, renamed, closed, revealed, or copied; tasks can be searched, renamed, pinned, and archived.
208
+
209
+ Use `Ctrl+P` to switch projects, `Ctrl+K` for the command palette, and `Ctrl+N` for a new task; the title bar also supports back/forward navigation. Interrupted work gets a recovery banner after an abnormal exit. The **Git Changes** view provides per-file diffs, stage/unstage/discard, commits, and `/review` handoff. Streaming Markdown, permission confirmations, and Skills, MCP, memory, and model management remain available.
208
210
 
209
211
  The **E2E** module in the sidebar drives a rough requirement or an existing design export through the full delivery pipeline: it generates a PRD and an interactive prototype for review, then moves into D2C visual implementation (Vue 3 / React) under `src/d2c-output/<task>/`. A Vite dev server starts automatically for pixel-level comparison, producing a visual-fidelity score and a structured diff report (region offsets, color deviations, font differences); the results workbench offers overlay, curtain, flicker, and heatmap modes, an SVG annotation layer, and a severity-sorted issue list. After visual review, a Swagger/OpenAPI contract is generated or imported to auto-create Axios wrappers and an Express mock server, followed by autonomous interactive acceptance and scored delivery.
210
212
 
package/README.zh-CN.md CHANGED
@@ -204,7 +204,9 @@ npm run desktop:pack # Windows 免安装目录
204
204
  npm run desktop:dist # Windows NSIS 安装包
205
205
  ```
206
206
 
207
- 桌面端提供项目和会话切换、流式 Markdown、工具与 Diff 展示、权限确认、任务状态,以及 Skill、MCP、记忆和模型管理。
207
+ 桌面端可以同时保持多个项目打开,同一项目也可并行运行最多 4 个独立任务;切换项目或任务不会中止后台执行。运行中的任务显示动态活动标记;完成、失败、等待确认和异常中断会进入持久活动中心并触发系统通知,未读完成项保留蓝点。项目支持置顶、别名、关闭、定位和复制路径,任务支持搜索、重命名、置顶和归档。
208
+
209
+ 使用 `Ctrl+P` 快速切换项目、`Ctrl+K` 打开命令面板、`Ctrl+N` 新建任务,标题栏支持前进/后退。异常退出后会出现恢复条。**Git 变更**视图提供逐文件 Diff、暂存、取消暂存、还原、提交及 `/review` 联动。桌面端还提供流式 Markdown、权限确认、Skill、MCP、记忆和模型管理。
208
210
 
209
211
  侧栏的 **E2E** 模块覆盖从粗需求到可验收成果物的完整交付链路:从粗需求生成 PRD 与可交互原型(支持审阅与退回),确认后进入 D2C 视觉还原(Vue 3 / React),自动启动 Vite dev server 进行像素级对比,输出视觉还原度评分与结构化差异报告,并提供叠加、帘幕、闪烁与热力图等对比模式、SVG 标注层和按严重度排序的问题列表;视觉审阅通过后,自动生成或导入 Swagger/OpenAPI 契约以创建 Axios 封装与 Express mock 服务,随后进行自主交互验收,最终完成评分与成果物交付。
210
212
 
@@ -16,7 +16,7 @@ import {
16
16
  redactErrorText,
17
17
  transcriptReducer,
18
18
  withTimeout
19
- } from "./chunk-KL4UHJ2Q.js";
19
+ } from "./chunk-Q5EQ4T3F.js";
20
20
  import "./chunk-IG4Z23CL.js";
21
21
  import {
22
22
  Box_default,
@@ -4,7 +4,7 @@ import {
4
4
  message,
5
5
  redactErrorText,
6
6
  redactSecrets
7
- } from "./chunk-KL4UHJ2Q.js";
7
+ } from "./chunk-Q5EQ4T3F.js";
8
8
 
9
9
  // src/rpc/server.ts
10
10
  import { createInterface } from "readline";
@@ -4952,18 +4952,25 @@ function startupLockPath(address) {
4952
4952
  async function spawnProductionBroker(address) {
4953
4953
  const entry2 = process.argv[1];
4954
4954
  if (entry2 === void 0) throw new Error("Cannot auto-start pals broker without a CLI entry point");
4955
+ return spawnPalBrokerProcess({ address, executable: process.execPath, args: [entry2, "--pals-broker", address] });
4956
+ }
4957
+ async function spawnPalBrokerProcess(options) {
4958
+ if (options.executable.trim() === "" || options.args.length === 0) throw new Error("Pals broker process requires an executable and entry arguments");
4959
+ const address = options.address;
4955
4960
  const lockPath = startupLockPath(address);
4956
4961
  const lock = await acquirePalFileLock({ path: lockPath, endpointLive: () => canConnect2(address) });
4957
4962
  if (lock === void 0) return;
4958
4963
  try {
4959
- const child = spawn3(process.execPath, [entry2, "--pals-broker", address], {
4964
+ const child = spawn3(options.executable, [...options.args], {
4960
4965
  detached: true,
4961
4966
  stdio: "ignore",
4962
- windowsHide: true
4967
+ windowsHide: true,
4968
+ ...options.environment === void 0 ? {} : { env: options.environment }
4963
4969
  });
4964
4970
  child.unref();
4965
4971
  const deadline = Date.now() + 5e3;
4966
4972
  while (Date.now() < deadline && !await canConnect2(address)) await delay(25);
4973
+ if (!await canConnect2(address)) throw new Error(`Pals broker process did not become ready at '${address}'`);
4967
4974
  } finally {
4968
4975
  await lock.release();
4969
4976
  }
@@ -20213,6 +20220,51 @@ var INTERRUPTED_TASK_PLAN_CONTEXT = [
20213
20220
  "Reassess the current query independently.",
20214
20221
  "If the current work needs a plan, create a fresh one with TaskPlan before using TaskUpdate."
20215
20222
  ].join(" ");
20223
+ function recoverablePalClient(client, onRecovered) {
20224
+ let presence;
20225
+ let starting;
20226
+ const ensure = () => {
20227
+ if (presence !== void 0) return Promise.resolve(presence);
20228
+ if (starting !== void 0) return starting;
20229
+ starting = client.start().then((value) => {
20230
+ presence = value;
20231
+ onRecovered();
20232
+ return value;
20233
+ }).finally(() => {
20234
+ starting = void 0;
20235
+ });
20236
+ return starting;
20237
+ };
20238
+ const invoke = async (run) => {
20239
+ await ensure();
20240
+ try {
20241
+ return await run();
20242
+ } catch (error) {
20243
+ const detail = message(error);
20244
+ if (!/not connected|connection closed|ECONNRESET|ECONNREFUSED|EPIPE/i.test(detail)) throw error;
20245
+ presence = void 0;
20246
+ await ensure();
20247
+ return run();
20248
+ }
20249
+ };
20250
+ return {
20251
+ start: ensure,
20252
+ list: () => invoke(() => client.list()),
20253
+ rename: (alias) => invoke(() => client.rename(alias)),
20254
+ sendTask: (target, value) => invoke(() => client.sendTask(target, value)),
20255
+ sendChat: (target, value) => invoke(() => client.sendChat(target, value)),
20256
+ startCoWork: (input) => invoke(() => client.startCoWork(input)),
20257
+ coWorkAction: (action) => invoke(() => client.coWorkAction(action)),
20258
+ coWorkStatus: (coWorkId) => invoke(() => client.coWorkStatus(coWorkId)),
20259
+ integrateCoWork: (input) => invoke(() => client.integrateCoWork(input)),
20260
+ cancelCoWork: (coWorkId, reason) => invoke(() => client.cancelCoWork(coWorkId, reason)),
20261
+ subscribe: (listener) => client.subscribe(listener),
20262
+ close: async () => {
20263
+ presence = void 0;
20264
+ await client.close();
20265
+ }
20266
+ };
20267
+ }
20216
20268
  var DEFAULT_SHUTDOWN_STEP_TIMEOUT_MS = 3e3;
20217
20269
  function createPromptEnvironment(input = {}) {
20218
20270
  const now = input.now ?? /* @__PURE__ */ new Date();
@@ -20533,6 +20585,7 @@ async function createProductionRuntime(options) {
20533
20585
  tools.push(evolveService.toolDefinition());
20534
20586
  let sleepScheduler;
20535
20587
  let collaborationClient;
20588
+ let collaborationUnavailableDiagnostic;
20536
20589
  let unsubscribeCollaboration;
20537
20590
  let collaborationSession;
20538
20591
  let deliverCollaborationEvent;
@@ -20575,7 +20628,7 @@ async function createProductionRuntime(options) {
20575
20628
  userScope: home,
20576
20629
  runtimeDir: join24(tmpdir3(), `flavor-code-pals-${createHash17("sha256").update(home).digest("hex").slice(0, 16)}`)
20577
20630
  });
20578
- collaborationClient = collaboration.client ?? collaboration.createClient?.({
20631
+ const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
20579
20632
  instanceId: collaboration.instanceId,
20580
20633
  alias,
20581
20634
  projectPath: workspace
@@ -20583,8 +20636,13 @@ async function createProductionRuntime(options) {
20583
20636
  address,
20584
20637
  authHome: home,
20585
20638
  registration: { id: collaboration.instanceId, alias, projectPath: workspace },
20586
- startBroker: () => ensurePalBrokerRunning({ address })
20639
+ startBroker: () => ensurePalBrokerRunning({ address, ...collaboration.startBroker === void 0 ? {} : { startBroker: () => collaboration.startBroker(address) } })
20587
20640
  });
20641
+ collaborationClient = collaboration.optional ? recoverablePalClient(baseCollaborationClient, () => {
20642
+ if (collaborationUnavailableDiagnostic === void 0) return;
20643
+ remove(diagnostics, collaborationUnavailableDiagnostic);
20644
+ collaborationUnavailableDiagnostic = void 0;
20645
+ }) : baseCollaborationClient;
20588
20646
  deliverCollaborationEvent = async (event) => {
20589
20647
  const session2 = collaborationSession;
20590
20648
  const client = collaborationClient;
@@ -20639,11 +20697,19 @@ async function createProductionRuntime(options) {
20639
20697
  }
20640
20698
  };
20641
20699
  unsubscribeCollaboration = collaborationClient.subscribe(enqueueCollaborationEvent);
20642
- tools.push(...createPalsTools(collaborationClient, {
20643
- selfId: collaboration.instanceId,
20644
- shareGuard: new CollaborationShareGuard({ redact: (value) => redactSecrets(value, secrets) })
20645
- }));
20646
- await collaborationClient.start();
20700
+ try {
20701
+ await collaborationClient.start();
20702
+ } catch (error) {
20703
+ if (!collaboration.optional) throw error;
20704
+ collaborationUnavailableDiagnostic = `Pals unavailable: ${message(error)}`;
20705
+ diagnostics.push(collaborationUnavailableDiagnostic);
20706
+ }
20707
+ if (collaborationClient !== void 0) {
20708
+ tools.push(...createPalsTools(collaborationClient, {
20709
+ selfId: collaboration.instanceId,
20710
+ shareGuard: new CollaborationShareGuard({ redact: (value) => redactSecrets(value, secrets) })
20711
+ }));
20712
+ }
20647
20713
  }
20648
20714
  const syncMcpTools = () => {
20649
20715
  for (const tool of mcpTools) remove(tools, tool);
@@ -21400,6 +21466,7 @@ async function createProductionRuntime(options) {
21400
21466
  }),
21401
21467
  rename: (alias) => collaborationClient.rename(alias),
21402
21468
  info: (target) => resolveCollaborationTarget(target),
21469
+ sendChat: (target, message2) => collaborationClient.sendChat(target, message2),
21403
21470
  sendTask: (target, goal) => collaborationClient.sendTask(target, goal),
21404
21471
  startCoWork: async (target, goal) => {
21405
21472
  const recipient = await resolveCollaborationTarget(target);
@@ -21660,6 +21727,7 @@ ${summary2}`;
21660
21727
  return sessionHistory.checkpoint(tagged, harness.main.context.snapshot());
21661
21728
  },
21662
21729
  tree: () => sessionHistory.tree(),
21730
+ historyLeaf: () => sessionHistory.leafId,
21663
21731
  rewind: async (nodeId) => {
21664
21732
  await sessionHistory.rewind(nodeId, harness.main.context.snapshot());
21665
21733
  await persist();
@@ -21848,7 +21916,7 @@ ${renderMemoryDocument(entries)}`;
21848
21916
  approvals,
21849
21917
  memoryReviews,
21850
21918
  restoredTranscript,
21851
- jobs: { list: () => jobs.list(), subscribe: (listener) => jobs.subscribe(listener) },
21919
+ jobs: { list: () => jobs.list(), read: (id, owner2, cursor) => jobs.read(id, owner2, cursor), subscribe: (listener) => jobs.subscribe(listener) },
21852
21920
  get sessionId() {
21853
21921
  return sessionId;
21854
21922
  },
@@ -22691,6 +22759,7 @@ export {
22691
22759
  COMMAND_DESCRIPTIONS,
22692
22760
  AgentMessageQueue,
22693
22761
  FlavorSession,
22762
+ classifyMemoryHeat,
22694
22763
  MemoryStore,
22695
22764
  buildDockerInvocation,
22696
22765
  DockerExecutionEnvironment,
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
3
3
  import {
4
4
  FlavorRpcServer,
5
5
  TraceRecorder
6
- } from "./chunk-IX65GYUF.js";
6
+ } from "./chunk-DFSMJ5SX.js";
7
7
  import {
8
8
  staticTaskLines
9
9
  } from "./chunk-KKIBYDJI.js";
@@ -14,6 +14,7 @@ import {
14
14
  MemoryStore,
15
15
  PluginManifestSchema,
16
16
  SkillRegistry,
17
+ classifyMemoryHeat,
17
18
  createExecutionEnvironment,
18
19
  createProductionRuntime,
19
20
  executeShell,
@@ -22,7 +23,7 @@ import {
22
23
  packageVersion,
23
24
  redactErrorText,
24
25
  runPalBroker
25
- } from "./chunk-KL4UHJ2Q.js";
26
+ } from "./chunk-Q5EQ4T3F.js";
26
27
  import {
27
28
  McpServerConfigSchema,
28
29
  McpServerNameSchema,
@@ -283,11 +284,22 @@ var ProjectMemoryManager = class {
283
284
  this.#store = store;
284
285
  this.#enabled = enabled;
285
286
  }
286
- async snapshot() {
287
+ async snapshot(now = /* @__PURE__ */ new Date()) {
288
+ const references = this.#enabled ? await this.#store.references() : [];
287
289
  return {
288
290
  enabled: this.#enabled,
289
291
  path: this.#store.path,
290
- entries: this.#enabled ? await this.#store.list() : []
292
+ entries: references.map((reference) => {
293
+ const lastRecalledAt = Object.values(reference.recalls).sort().at(-1);
294
+ return {
295
+ id: reference.id,
296
+ type: reference.type,
297
+ content: reference.summary,
298
+ recallTotal: reference.recallTotal,
299
+ heat: classifyMemoryHeat(reference, now),
300
+ ...lastRecalledAt === void 0 ? {} : { lastRecalledAt }
301
+ };
302
+ })
291
303
  };
292
304
  }
293
305
  async remember(candidate) {
@@ -304,6 +316,10 @@ var ProjectMemoryManager = class {
304
316
  this.#assertEnabled();
305
317
  return this.#store.delete(id);
306
318
  }
319
+ async deleteCold(now = /* @__PURE__ */ new Date()) {
320
+ this.#assertEnabled();
321
+ return this.#store.forgetCold(now);
322
+ }
307
323
  #assertEnabled() {
308
324
  if (!this.#enabled) throw new Error("Long-term memory is disabled for this project");
309
325
  }
@@ -861,7 +877,7 @@ async function runInteractiveCli(props) {
861
877
  const [{ render, AlternateScreen }, { createElement }, { App }] = await Promise.all([
862
878
  import("./claude-ink-ORUA7GWG.js"),
863
879
  import("react"),
864
- import("./app-FOPTLLK5.js")
880
+ import("./app-F6FEEF46.js")
865
881
  ]);
866
882
  const instance = await render(createElement(
867
883
  AlternateScreen,