zenflo 0.11.5 → 0.11.7

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.
@@ -3,7 +3,7 @@
3
3
  var chalk = require('chalk');
4
4
  var os = require('node:os');
5
5
  var node_crypto = require('node:crypto');
6
- var types = require('./types-BowvGBcM.cjs');
6
+ var types = require('./types-Dvhor4zW.cjs');
7
7
  var node_child_process = require('node:child_process');
8
8
  var node_path = require('node:path');
9
9
  var node_readline = require('node:readline');
@@ -216,18 +216,29 @@ function shouldIncludeCoAuthoredBy() {
216
216
  }
217
217
 
218
218
  const BASE_SYSTEM_PROMPT = (() => trimIdent(`
219
- ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
219
+ ALWAYS when you start a new chat - you must call a tool "mcp__zenflo__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
220
+
221
+ \u26A0\uFE0F CRITICAL: Use Zen Mode MCP, NOT TodoWrite tool
222
+
223
+ At the START of EVERY session, you MUST:
224
+ 1. Call mcp__zen_mode__list_tasks to see existing tasks
225
+ 2. Use mcp__zen_mode__create_task to create new tasks for the session
226
+ 3. Use mcp__zen_mode__update_task to update status as you work
227
+ 4. NEVER use TodoWrite tool - it doesn't sync across devices
228
+
229
+ Task Priorities: LOW, MEDIUM, HIGH, URGENT
230
+ Task Status: TODO, IN_PROGRESS, DONE, CANCELLED
220
231
  `))();
221
232
  const CO_AUTHORED_CREDITS = (() => trimIdent(`
222
- When making commit messages, instead of just giving co-credit to Claude, also give credit to Happy like so:
233
+ When making commit messages, instead of just giving co-credit to Claude, also give credit to ZenFlo like so:
223
234
 
224
235
  <main commit message>
225
236
 
226
237
  Generated with [Claude Code](https://claude.ai/code)
227
- via [Happy](https://happy.engineering)
238
+ via [ZenFlo](https://zenflo.app)
228
239
 
229
240
  Co-Authored-By: Claude <noreply@anthropic.com>
230
- Co-Authored-By: Happy <yesreply@happy.engineering>
241
+ Co-Authored-By: ZenFlo <yesreply@zenflo.app>
231
242
  `))();
232
243
  const systemPrompt = (() => {
233
244
  const includeCoAuthored = shouldIncludeCoAuthoredBy();
@@ -279,7 +290,10 @@ async function claudeLocal(opts) {
279
290
  }
280
291
  };
281
292
  try {
282
- process.stdin.pause();
293
+ const isStreamJsonMode = opts.claudeArgs?.includes("--input-format") && opts.claudeArgs?.includes("stream-json");
294
+ if (!isStreamJsonMode) {
295
+ process.stdin.pause();
296
+ }
283
297
  await new Promise((r, reject) => {
284
298
  const args = [];
285
299
  if (startFrom) {
@@ -296,7 +310,7 @@ async function claudeLocal(opts) {
296
310
  args.push(...opts.claudeArgs);
297
311
  }
298
312
  if (!claudeCliPath || !fs.existsSync(claudeCliPath)) {
299
- throw new Error("Claude local launcher not found. Please ensure HAPPY_PROJECT_ROOT is set correctly for development.");
313
+ throw new Error("Claude local launcher not found. Please ensure ZENFLO_PROJECT_ROOT is set correctly for development.");
300
314
  }
301
315
  const env = {
302
316
  ...process.env,
@@ -304,10 +318,16 @@ async function claudeLocal(opts) {
304
318
  };
305
319
  const child = node_child_process.spawn("node", [claudeCliPath, ...args], {
306
320
  stdio: ["inherit", "inherit", "inherit", "pipe"],
307
- signal: opts.abort,
308
321
  cwd: opts.path,
309
322
  env
310
323
  });
324
+ const abortHandler = () => {
325
+ if (!child.killed) {
326
+ types.logger.debug("[ClaudeLocal] Abort signal received, killing child process");
327
+ child.kill("SIGTERM");
328
+ }
329
+ };
330
+ opts.abort.addEventListener("abort", abortHandler);
311
331
  if (child.stdio[3]) {
312
332
  const rl = node_readline.createInterface({
313
333
  input: child.stdio[3],
@@ -379,7 +399,10 @@ async function claudeLocal(opts) {
379
399
  });
380
400
  } finally {
381
401
  watcher.close();
382
- process.stdin.resume();
402
+ const isStreamJsonMode = opts.claudeArgs?.includes("--input-format") && opts.claudeArgs?.includes("stream-json");
403
+ if (!isStreamJsonMode) {
404
+ process.stdin.resume();
405
+ }
383
406
  if (stopThinkingTimeout) {
384
407
  clearTimeout(stopThinkingTimeout);
385
408
  stopThinkingTimeout = null;
@@ -563,7 +586,7 @@ async function createSessionScanner(opts) {
563
586
  await sync.invalidateAndAwait();
564
587
  sync.stop();
565
588
  },
566
- onNewSession: (sessionId) => {
589
+ onNewSession: async (sessionId) => {
567
590
  if (currentSessionId === sessionId) {
568
591
  types.logger.debug(`[SESSION_SCANNER] New session: ${sessionId} is the same as the current session, skipping`);
569
592
  return;
@@ -581,6 +604,19 @@ async function createSessionScanner(opts) {
581
604
  }
582
605
  types.logger.debug(`[SESSION_SCANNER] New session: ${sessionId}`);
583
606
  currentSessionId = sessionId;
607
+ try {
608
+ const existingMessages = await readSessionLog(projectDir, sessionId);
609
+ types.logger.debug(`[SESSION_SCANNER] Found ${existingMessages.length} existing messages in new session ${sessionId}`);
610
+ for (const message of existingMessages) {
611
+ const key = messageKey(message);
612
+ if (!processedMessageKeys.has(key)) {
613
+ processedMessageKeys.add(key);
614
+ opts.onMessage(message);
615
+ }
616
+ }
617
+ } catch (err) {
618
+ types.logger.debug(`[SESSION_SCANNER] Error reading existing messages: ${err}`);
619
+ }
584
620
  sync.invalidate();
585
621
  }
586
622
  };
@@ -594,6 +630,8 @@ function messageKey(message) {
594
630
  return "summary: " + message.leafUuid + ": " + message.summary;
595
631
  } else if (message.type === "system") {
596
632
  return message.uuid;
633
+ } else if (message.type === "queue-operation") {
634
+ return "queue-operation: " + message.timestamp + ": " + message.operation;
597
635
  } else {
598
636
  throw Error();
599
637
  }
@@ -618,7 +656,8 @@ async function readSessionLog(projectDir, sessionId) {
618
656
  let message = JSON.parse(l);
619
657
  let parsed = types.RawJSONLinesSchema.safeParse(message);
620
658
  if (!parsed.success) {
621
- types.logger.debugLargeJson(`[SESSION_SCANNER] Failed to parse message`, message);
659
+ types.logger.debug(`[SESSION_SCANNER] Failed to parse message - Zod error:`, parsed.error.errors);
660
+ types.logger.debugLargeJson(`[SESSION_SCANNER] Failed message content:`, message);
622
661
  continue;
623
662
  }
624
663
  messages.push(parsed.data);
@@ -635,7 +674,7 @@ async function claudeLocalLauncher(session) {
635
674
  sessionId: session.sessionId,
636
675
  workingDirectory: session.path,
637
676
  onMessage: (message) => {
638
- if (message.type !== "summary") {
677
+ if (message.type !== "summary" && message.type !== "queue-operation") {
639
678
  session.client.sendClaudeSessionMessage(message);
640
679
  }
641
680
  }
@@ -981,7 +1020,7 @@ class AbortError extends Error {
981
1020
  }
982
1021
  }
983
1022
 
984
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-6-qKdQ7W.cjs', document.baseURI).href)));
1023
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-HBSmEvnF.cjs', document.baseURI).href)));
985
1024
  const __dirname$1 = node_path.join(__filename$1, "..");
986
1025
  function getDefaultClaudeCodePath() {
987
1026
  return node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
@@ -3210,7 +3249,7 @@ function hashObject(obj, options, encoding = "hex") {
3210
3249
  let caffeinateProcess = null;
3211
3250
  function startCaffeinate() {
3212
3251
  if (types.configuration.disableCaffeinate) {
3213
- types.logger.debug("[caffeinate] Caffeinate disabled via HAPPY_DISABLE_CAFFEINATE environment variable");
3252
+ types.logger.debug("[caffeinate] Caffeinate disabled via ZENFLO_DISABLE_CAFFEINATE environment variable");
3214
3253
  return false;
3215
3254
  }
3216
3255
  if (process.platform !== "darwin") {
@@ -3354,7 +3393,7 @@ async function daemonPost(path, body) {
3354
3393
  };
3355
3394
  }
3356
3395
  try {
3357
- const timeout = process.env.HAPPY_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.HAPPY_DAEMON_HTTP_TIMEOUT) : 1e4;
3396
+ const timeout = process.env.ZENFLO_DAEMON_HTTP_TIMEOUT ? parseInt(process.env.ZENFLO_DAEMON_HTTP_TIMEOUT) : 1e4;
3358
3397
  const response = await fetch(`http://127.0.0.1:${state.httpPort}${path}`, {
3359
3398
  method: "POST",
3360
3399
  headers: { "Content-Type": "application/json" },
@@ -3553,16 +3592,14 @@ function getEnvironmentInfo() {
3553
3592
  return {
3554
3593
  PWD: process.env.PWD,
3555
3594
  ZENFLO_HOME_DIR: process.env.ZENFLO_HOME_DIR,
3556
- HAPPY_HOME_DIR: process.env.HAPPY_HOME_DIR,
3557
3595
  ZENFLO_SERVER_URL: process.env.ZENFLO_SERVER_URL,
3558
- HAPPY_SERVER_URL: process.env.HAPPY_SERVER_URL,
3559
- HAPPY_PROJECT_ROOT: process.env.HAPPY_PROJECT_ROOT,
3596
+ ZENFLO_PROJECT_ROOT: process.env.ZENFLO_PROJECT_ROOT,
3560
3597
  DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING: process.env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING,
3561
3598
  NODE_ENV: process.env.NODE_ENV,
3562
3599
  DEBUG: process.env.DEBUG,
3563
3600
  workingDirectory: process.cwd(),
3564
3601
  processArgv: process.argv,
3565
- happyDir: types.configuration?.happyHomeDir,
3602
+ happyDir: types.configuration?.zenfloHomeDir,
3566
3603
  serverUrl: types.configuration?.serverUrl,
3567
3604
  logsDir: types.configuration?.logsDir,
3568
3605
  processPid: process.pid,
@@ -3611,15 +3648,13 @@ async function runDoctorCommand(filter) {
3611
3648
  console.log(`CLI Exists: ${fs.existsSync(cliEntrypoint) ? chalk.green("\u2713 Yes") : chalk.red("\u274C No")}`);
3612
3649
  console.log("");
3613
3650
  console.log(chalk.bold("\u2699\uFE0F Configuration"));
3614
- console.log(`Happy Home: ${chalk.blue(types.configuration.happyHomeDir)}`);
3651
+ console.log(`Happy Home: ${chalk.blue(types.configuration.zenfloHomeDir)}`);
3615
3652
  console.log(`Server URL: ${chalk.blue(types.configuration.serverUrl)}`);
3616
3653
  console.log(`Logs Dir: ${chalk.blue(types.configuration.logsDir)}`);
3617
3654
  console.log(chalk.bold("\n\u{1F30D} Environment Variables"));
3618
3655
  const env = getEnvironmentInfo();
3619
3656
  console.log(`ZENFLO_HOME_DIR: ${env.ZENFLO_HOME_DIR ? chalk.green(env.ZENFLO_HOME_DIR) : chalk.gray("not set")}`);
3620
- console.log(`HAPPY_HOME_DIR (legacy): ${env.HAPPY_HOME_DIR ? chalk.yellow(env.HAPPY_HOME_DIR) : chalk.gray("not set")}`);
3621
3657
  console.log(`ZENFLO_SERVER_URL: ${env.ZENFLO_SERVER_URL ? chalk.green(env.ZENFLO_SERVER_URL) : chalk.gray("not set")}`);
3622
- console.log(`HAPPY_SERVER_URL (legacy): ${env.HAPPY_SERVER_URL ? chalk.yellow(env.HAPPY_SERVER_URL) : chalk.gray("not set")}`);
3623
3658
  console.log(`DANGEROUSLY_LOG_TO_SERVER: ${env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING ? chalk.yellow("ENABLED") : chalk.gray("not set")}`);
3624
3659
  console.log(`DEBUG: ${env.DEBUG ? chalk.green(env.DEBUG) : chalk.gray("not set")}`);
3625
3660
  console.log(`NODE_ENV: ${env.NODE_ENV ? chalk.green(env.NODE_ENV) : chalk.gray("not set")}`);
@@ -4036,7 +4071,7 @@ function startDaemonControlServer({
4036
4071
  stopSession,
4037
4072
  spawnSession,
4038
4073
  requestShutdown,
4039
- onHappySessionWebhook
4074
+ onZenfloSessionWebhook
4040
4075
  }) {
4041
4076
  return new Promise((resolve) => {
4042
4077
  const app = fastify({
@@ -4062,7 +4097,7 @@ function startDaemonControlServer({
4062
4097
  }, async (request) => {
4063
4098
  const { sessionId, metadata } = request.body;
4064
4099
  types.logger.debug(`[CONTROL SERVER] Session started: ${sessionId}`);
4065
- onHappySessionWebhook(sessionId, metadata);
4100
+ onZenfloSessionWebhook(sessionId, metadata);
4066
4101
  return { status: "ok" };
4067
4102
  });
4068
4103
  typed.post("/list", {
@@ -4201,10 +4236,10 @@ function startDaemonControlServer({
4201
4236
  const initialMachineMetadata = {
4202
4237
  host: os$1.hostname(),
4203
4238
  platform: os$1.platform(),
4204
- happyCliVersion: types.packageJson.version,
4239
+ zenfloCliVersion: types.packageJson.version,
4205
4240
  homeDir: os$1.homedir(),
4206
- happyHomeDir: types.configuration.happyHomeDir,
4207
- happyLibDir: types.projectPath()
4241
+ zenfloHomeDir: types.configuration.zenfloHomeDir,
4242
+ zenfloLibDir: types.projectPath()
4208
4243
  };
4209
4244
  async function startDaemon() {
4210
4245
  let requestShutdown;
@@ -4271,7 +4306,7 @@ async function startDaemon() {
4271
4306
  const pidToTrackedSession = /* @__PURE__ */ new Map();
4272
4307
  const pidToAwaiter = /* @__PURE__ */ new Map();
4273
4308
  const getCurrentChildren = () => Array.from(pidToTrackedSession.values());
4274
- const onHappySessionWebhook = (sessionId, sessionMetadata) => {
4309
+ const onZenfloSessionWebhook = (sessionId, sessionMetadata) => {
4275
4310
  types.logger.debugLargeJson(`[DAEMON RUN] Session reported`, sessionMetadata);
4276
4311
  const pid = sessionMetadata.hostPid;
4277
4312
  if (!pid) {
@@ -4293,7 +4328,7 @@ async function startDaemon() {
4293
4328
  }
4294
4329
  } else if (!existingSession) {
4295
4330
  const trackedSession = {
4296
- startedBy: "happy directly - likely by user from terminal",
4331
+ startedBy: "zenflo directly - likely by user from terminal",
4297
4332
  zenfloSessionId: sessionId,
4298
4333
  zenfloSessionMetadataFromLocalWebhook: sessionMetadata,
4299
4334
  pid
@@ -4358,8 +4393,9 @@ async function startDaemon() {
4358
4393
  }
4359
4394
  }
4360
4395
  const args = [
4361
- options.agent === "claude" ? "claude" : "codex",
4362
- "--happy-starting-mode",
4396
+ options.agent || "claude",
4397
+ // Use the agent directly (claude, codex, qwen, gemini)
4398
+ "--zenflo-starting-mode",
4363
4399
  "remote",
4364
4400
  "--started-by",
4365
4401
  "daemon"
@@ -4474,8 +4510,8 @@ async function startDaemon() {
4474
4510
  getChildren: getCurrentChildren,
4475
4511
  stopSession,
4476
4512
  spawnSession,
4477
- requestShutdown: () => requestShutdown("happy-cli"),
4478
- onHappySessionWebhook
4513
+ requestShutdown: () => requestShutdown("zenflo-cli"),
4514
+ onZenfloSessionWebhook
4479
4515
  });
4480
4516
  const fileState = {
4481
4517
  pid: process.pid,
@@ -4503,10 +4539,10 @@ async function startDaemon() {
4503
4539
  apiMachine.setRPCHandlers({
4504
4540
  spawnSession,
4505
4541
  stopSession,
4506
- requestShutdown: () => requestShutdown("happy-app")
4542
+ requestShutdown: () => requestShutdown("zenflo-app")
4507
4543
  });
4508
4544
  apiMachine.connect();
4509
- const heartbeatIntervalMs = parseInt(process.env.HAPPY_DAEMON_HEARTBEAT_INTERVAL || "60000");
4545
+ const heartbeatIntervalMs = parseInt(process.env.ZENFLO_DAEMON_HEARTBEAT_INTERVAL || "60000");
4510
4546
  let heartbeatRunning = false;
4511
4547
  const restartOnStaleVersionAndHeartbeat = setInterval(async () => {
4512
4548
  if (heartbeatRunning) {
@@ -4729,7 +4765,8 @@ function registerKillSessionHandler(rpcHandlerManager, killThisHappy) {
4729
4765
 
4730
4766
  async function runClaude(credentials, options = {}) {
4731
4767
  const workingDirectory = process.cwd();
4732
- const sessionTag = node_crypto.randomUUID();
4768
+ const isExtensionMode = options.claudeArgs?.includes("--input-format") && options.claudeArgs?.includes("stream-json");
4769
+ const sessionTag = isExtensionMode ? `extension-${hashObject({ workingDirectory, machineId: (await types.readSettings())?.machineId })}` : node_crypto.randomUUID();
4733
4770
  types.logger.debugLargeJson("[START] ZenFlo process started", getEnvironmentInfo());
4734
4771
  types.logger.debug(`[START] Options: startedBy=${options.startedBy}, startingMode=${options.startingMode}`);
4735
4772
  if (options.startedBy === "daemon" && options.startingMode === "local") {
@@ -4756,9 +4793,9 @@ async function runClaude(credentials, options = {}) {
4756
4793
  os: os.platform(),
4757
4794
  machineId,
4758
4795
  homeDir: os.homedir(),
4759
- happyHomeDir: types.configuration.happyHomeDir,
4760
- happyLibDir: types.projectPath(),
4761
- happyToolsDir: node_path.resolve(types.projectPath(), "tools", "unpacked"),
4796
+ zenfloHomeDir: types.configuration.zenfloHomeDir,
4797
+ zenfloLibDir: types.projectPath(),
4798
+ zenfloToolsDir: node_path.resolve(types.projectPath(), "tools", "unpacked"),
4762
4799
  startedFromDaemon: options.startedBy === "daemon",
4763
4800
  hostPid: process.pid,
4764
4801
  startedBy: options.startedBy || "terminal",
@@ -5029,7 +5066,7 @@ async function install$1() {
5029
5066
 
5030
5067
  <key>EnvironmentVariables</key>
5031
5068
  <dict>
5032
- <key>HAPPY_DAEMON_MODE</key>
5069
+ <key>ZENFLO_DAEMON_MODE</key>
5033
5070
  <string>true</string>
5034
5071
  </dict>
5035
5072
 
@@ -5194,7 +5231,7 @@ async function handleAuthLogin(args) {
5194
5231
  }
5195
5232
  }
5196
5233
  async function handleAuthLogout() {
5197
- const happyDir = types.configuration.happyHomeDir;
5234
+ const happyDir = types.configuration.zenfloHomeDir;
5198
5235
  const credentials = await types.readCredentials();
5199
5236
  if (!credentials) {
5200
5237
  console.log(chalk.yellow("Not currently authenticated"));
@@ -5250,7 +5287,7 @@ async function handleAuthStatus() {
5250
5287
  console.log(chalk.gray(' Run "happy auth login --force" to fix this'));
5251
5288
  }
5252
5289
  console.log(chalk.gray(`
5253
- Data directory: ${types.configuration.happyHomeDir}`));
5290
+ Data directory: ${types.configuration.zenfloHomeDir}`));
5254
5291
  try {
5255
5292
  const running = await checkIfDaemonRunningAndCleanupStaleState();
5256
5293
  if (running) {
@@ -5779,10 +5816,10 @@ ${chalk.bold("Examples:")}
5779
5816
  happy connect claude
5780
5817
  happy connect gemini
5781
5818
 
5782
- ${chalk.bold("Notes:")}
5819
+ ${chalk.bold("Notes:")}
5783
5820
  \u2022 You must be authenticated with Happy first (run 'happy auth login')
5784
5821
  \u2022 API keys are encrypted and stored securely in Happy cloud
5785
- \u2022 You can manage your stored keys at app.happy.engineering
5822
+ \u2022 You can manage your stored keys at app.combinedmemory.com
5786
5823
  `);
5787
5824
  }
5788
5825
  async function handleConnectVendor(vendor, displayName) {
@@ -5860,7 +5897,7 @@ async function handleConnectVendor(vendor, displayName) {
5860
5897
  return;
5861
5898
  } else if (subcommand === "codex") {
5862
5899
  try {
5863
- const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-qe9_J04U.cjs'); });
5900
+ const { runCodex } = await Promise.resolve().then(function () { return require('./runCodex-cXLrsovg.cjs'); });
5864
5901
  let startedBy = void 0;
5865
5902
  for (let i = 1; i < args.length; i++) {
5866
5903
  if (args[i] === "--started-by") {
@@ -6018,7 +6055,7 @@ ${chalk.bold("To clean up runaway processes:")} Use ${chalk.cyan("happy doctor c
6018
6055
  } else if (arg === "-v" || arg === "--version") {
6019
6056
  showVersion = true;
6020
6057
  unknownArgs.push(arg);
6021
- } else if (arg === "--happy-starting-mode") {
6058
+ } else if (arg === "--zenflo-starting-mode") {
6022
6059
  options.startingMode = z.z.enum(["local", "remote"]).parse(args[++i]);
6023
6060
  } else if (arg === "--yolo") {
6024
6061
  unknownArgs.push("--dangerously-skip-permissions");