daily-focus 1.2.0-dev.9 → 2.0.0-dev.14

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.
@@ -4,11 +4,10 @@ import {
4
4
  } from "./chunk-BC2W7NND.js";
5
5
 
6
6
  // src/env.ts
7
- import { readFileSync } from "node:fs";
7
+ import { existsSync, readFileSync } from "node:fs";
8
8
  import { homedir } from "node:os";
9
9
  import { isAbsolute, resolve } from "node:path";
10
10
  import { parseEnv } from "node:util";
11
- var DOTENV_PATH = resolve(import.meta.dirname, "..", ".env");
12
11
  var DEFAULT_DATA_DIR = "~/.daily-focus";
13
12
  var SETTINGS_FILE = "settings.json";
14
13
  var LAUNCH_SETTINGS = ["DAILY_FOCUS_DATA", "DAILY_FOCUS_PORT", "DAILY_FOCUS_HOST"];
@@ -18,20 +17,6 @@ function expandHome(p) {
18
17
  if (!p.includes("/")) return p;
19
18
  return isAbsolute(p) ? p : resolve(process.cwd(), p);
20
19
  }
21
- function readDotEnv(path = DOTENV_PATH) {
22
- let text;
23
- try {
24
- text = readFileSync(path, "utf8");
25
- } catch (err) {
26
- if (err.code === "ENOENT") return {};
27
- throw new Error(`could not read ${path}: ${err.message}`);
28
- }
29
- const parsed = {};
30
- for (const [key, value] of Object.entries(parseEnv(text.replace(/^/, "")))) {
31
- if (value !== void 0) parsed[key] = value;
32
- }
33
- return parsed;
34
- }
35
20
  function readSettingsFile(path) {
36
21
  let text;
37
22
  try {
@@ -60,35 +45,48 @@ function readSettingsFile(path) {
60
45
  const error = unreadable.length > 0 ? `${path} has values that aren't text, a number or true/false: ${unreadable.join(", ")}` : null;
61
46
  return { values, error };
62
47
  }
63
- function prefixed(record) {
64
- const kept = {};
65
- for (const [key, value] of Object.entries(record)) {
66
- if (key.startsWith("DAILY_FOCUS_") && value !== void 0) kept[key] = value;
48
+ function settingSources(env = process.env, fixed = {}) {
49
+ const launch = {};
50
+ for (const key of LAUNCH_SETTINGS) {
51
+ const value = env[key]?.trim();
52
+ if (value) launch[key] = value;
67
53
  }
68
- return kept;
69
- }
70
- function mergeEnv(fromFile, processEnv = process.env) {
71
- return { ...prefixed(fromFile), ...processEnv };
72
- }
73
- function envSources(processEnv = process.env, dotenv = readDotEnv()) {
74
- const merged = mergeEnv(dotenv, processEnv);
75
- const dataDir = expandHome((merged.DAILY_FOCUS_DATA ?? "").trim() || DEFAULT_DATA_DIR);
54
+ const dataDir = expandHome(launch.DAILY_FOCUS_DATA ?? DEFAULT_DATA_DIR);
76
55
  const settingsPath = resolve(dataDir, SETTINGS_FILE);
77
56
  const { values, error } = readSettingsFile(settingsPath);
78
- return { dotenv: prefixed(dotenv), settings: values, process: processEnv, settingsPath, settingsError: error };
57
+ return { launch, settings: values, fixed, settingsPath, settingsError: error };
79
58
  }
80
- function layerEnv(sources, settings = sources.settings) {
81
- return { ...sources.dotenv, ...withoutBlanks(settings), ...withoutBlanks(sources.process) };
82
- }
83
- function withoutBlanks(record) {
59
+ function layerSettings(sources, settings = sources.settings) {
84
60
  const kept = {};
85
- for (const [key, value] of Object.entries(record)) {
86
- if (value !== void 0 && value.trim() !== "") kept[key] = value;
61
+ for (const [key, value] of Object.entries(settings)) {
62
+ if (value.trim() !== "") kept[key] = value;
87
63
  }
88
- return kept;
64
+ return { ...kept, ...sources.fixed, ...sources.launch };
65
+ }
66
+ function loadSettings() {
67
+ return layerSettings(settingSources());
89
68
  }
90
- function loadEnv() {
91
- return layerEnv(envSources());
69
+ var OLD_DOTENV_PATH = resolve(import.meta.dirname, "..", ".env");
70
+ function oldDotenvWarning(path = OLD_DOTENV_PATH) {
71
+ if (!existsSync(path)) return null;
72
+ let keys;
73
+ try {
74
+ const parsed = parseEnv(readFileSync(path, "utf8").replace(/^/, ""));
75
+ keys = Object.keys(parsed).filter((key) => key.startsWith("DAILY_FOCUS_") && (parsed[key] ?? "").trim() !== "");
76
+ } catch {
77
+ keys = [];
78
+ }
79
+ if (keys.length === 0) return null;
80
+ const launch = keys.filter((key) => LAUNCH_SETTINGS.includes(key));
81
+ const rest = keys.filter((key) => !LAUNCH_SETTINGS.includes(key));
82
+ const parts = [`${path} is no longer read.`];
83
+ if (rest.length > 0) parts.push(`Set ${rest.join(", ")} on the settings page instead.`);
84
+ if (launch.length > 0) {
85
+ const flags = launch.map((key) => `--${key.slice("DAILY_FOCUS_".length).toLowerCase()}`);
86
+ parts.push(`Give ${flags.join(", ")} when starting it, or set ${launch.join(", ")} in the environment.`);
87
+ }
88
+ parts.push("Then delete the file.");
89
+ return parts.join(" ");
92
90
  }
93
91
 
94
92
  // src/time.ts
@@ -236,12 +234,12 @@ function ranToday(lastRunStartedAt, now) {
236
234
  }
237
235
 
238
236
  // src/config.ts
239
- import { existsSync } from "node:fs";
237
+ import { existsSync as existsSync2 } from "node:fs";
240
238
  import { resolve as resolve2 } from "node:path";
241
239
  var PROFILES = ["work", "personal"];
242
240
  var CLI_NAMES = ["claude", "codex"];
243
- function envInt(name, fallback, env) {
244
- const raw = env[name];
241
+ function intSetting(name, fallback, values) {
242
+ const raw = values[name];
245
243
  if (raw === void 0 || raw.trim() === "") return fallback;
246
244
  const n = Number(raw);
247
245
  if (!Number.isFinite(n)) {
@@ -249,15 +247,15 @@ function envInt(name, fallback, env) {
249
247
  }
250
248
  return n;
251
249
  }
252
- function envBounded(name, fallback, min, max, env) {
253
- const n = envInt(name, fallback, env);
250
+ function boundedSetting(name, fallback, min, max, values) {
251
+ const n = intSetting(name, fallback, values);
254
252
  if (!Number.isInteger(n) || n < min || n > max) {
255
- throw new Error(`${name} must be a whole number from ${min} to ${max}, got ${JSON.stringify(env[name])}`);
253
+ throw new Error(`${name} must be a whole number from ${min} to ${max}, got ${JSON.stringify(values[name])}`);
256
254
  }
257
255
  return n;
258
256
  }
259
- function envNameList(name, env) {
260
- const raw = env[name];
257
+ function nameListSetting(name, values) {
258
+ const raw = values[name];
261
259
  if (raw === void 0) return [];
262
260
  const names = [];
263
261
  for (const entry of raw.split(",")) {
@@ -266,8 +264,8 @@ function envNameList(name, env) {
266
264
  }
267
265
  return names;
268
266
  }
269
- function envList(name, env) {
270
- const raw = env[name];
267
+ function listSetting(name, values) {
268
+ const raw = values[name];
271
269
  if (raw === void 0) return [];
272
270
  return raw.split(/[\s,]+/).map((entry) => entry.trim()).filter((entry) => entry !== "");
273
271
  }
@@ -281,80 +279,82 @@ function parseScope(entries) {
281
279
  }
282
280
  return qualifiers;
283
281
  }
284
- function envFlag(name, fallback, env) {
285
- const flag = (env[name] ?? "").trim().toLowerCase();
282
+ function flagSetting(name, fallback, values) {
283
+ const flag = (values[name] ?? "").trim().toLowerCase();
286
284
  if (flag === "") return fallback;
287
285
  return !["off", "false", "0", "no"].includes(flag);
288
286
  }
289
- function envGitHub(env) {
290
- const pollMinutes = envInt("DAILY_FOCUS_GITHUB_POLL_MINUTES", 5, env);
287
+ function gitHubConfig(values) {
288
+ const pollMinutes = intSetting("DAILY_FOCUS_GITHUB_POLL_MINUTES", 5, values);
291
289
  if (pollMinutes < 1) {
292
290
  throw new Error(`DAILY_FOCUS_GITHUB_POLL_MINUTES must be at least 1, got ${pollMinutes}`);
293
291
  }
294
292
  return {
295
- enabled: envFlag("DAILY_FOCUS_GITHUB", true, env),
296
- accounts: envList("DAILY_FOCUS_GITHUB_ACCOUNTS", env),
297
- scope: parseScope(envList("DAILY_FOCUS_GITHUB_SCOPE", env)),
298
- mergeGateChecks: envNameList("DAILY_FOCUS_GITHUB_MERGE_GATE_CHECKS", env),
293
+ enabled: flagSetting("DAILY_FOCUS_GITHUB", true, values),
294
+ accounts: listSetting("DAILY_FOCUS_GITHUB_ACCOUNTS", values),
295
+ scope: parseScope(listSetting("DAILY_FOCUS_GITHUB_SCOPE", values)),
296
+ mergeGateChecks: nameListSetting("DAILY_FOCUS_GITHUB_MERGE_GATE_CHECKS", values),
299
297
  pollMinutes,
300
- ghPath: expandHome((env.DAILY_FOCUS_GH ?? "").trim() || "gh")
298
+ ghPath: expandHome((values.DAILY_FOCUS_GH ?? "").trim() || "gh")
301
299
  };
302
300
  }
303
- function envCalendar(env) {
304
- const pollMinutes = envInt("DAILY_FOCUS_CALENDAR_POLL_MINUTES", 5, env);
301
+ function calendarConfig(values) {
302
+ const pollMinutes = intSetting("DAILY_FOCUS_CALENDAR_POLL_MINUTES", 5, values);
305
303
  if (pollMinutes < 1) {
306
304
  throw new Error(`DAILY_FOCUS_CALENDAR_POLL_MINUTES must be at least 1, got ${pollMinutes}`);
307
305
  }
308
306
  return {
309
- enabled: envFlag("DAILY_FOCUS_CALENDAR", true, env),
307
+ enabled: flagSetting("DAILY_FOCUS_CALENDAR", true, values),
310
308
  // Commas only, as for merge gate checks: calendar names contain spaces.
311
- names: envNameList("DAILY_FOCUS_CALENDARS", env),
312
- addresses: envNameList("DAILY_FOCUS_CALENDAR_ADDRESSES", env),
309
+ names: nameListSetting("DAILY_FOCUS_CALENDARS", values),
310
+ addresses: nameListSetting("DAILY_FOCUS_CALENDAR_ADDRESSES", values),
313
311
  pollMinutes,
314
312
  // What the user set, then the helper the Mac app carries, then the one
315
313
  // `daily-focus build-calendar` installs, then a checkout's own build.
316
314
  appPath: expandHome(
317
- (env.DAILY_FOCUS_CALENDAR_APP ?? "").trim() || [APP_CALENDAR_APP, USER_CALENDAR_APP].find((path) => path !== null && existsSync(path)) || resolve2(import.meta.dirname, "..", "tools/dfcal/build/Daily Focus Calendar.app")
315
+ (values.DAILY_FOCUS_CALENDAR_APP ?? "").trim() || [APP_CALENDAR_APP, USER_CALENDAR_APP].find((path) => path !== null && existsSync2(path)) || resolve2(import.meta.dirname, "..", "tools/dfcal/build/Daily Focus Calendar.app")
318
316
  )
319
317
  };
320
318
  }
321
- function envJira(env, profile) {
322
- const pollMinutes = envInt("DAILY_FOCUS_JIRA_POLL_MINUTES", 15, env);
319
+ function jiraConfig(values, profile) {
320
+ const pollMinutes = intSetting("DAILY_FOCUS_JIRA_POLL_MINUTES", 15, values);
323
321
  if (pollMinutes < 1) {
324
322
  throw new Error(`DAILY_FOCUS_JIRA_POLL_MINUTES must be at least 1, got ${pollMinutes}`);
325
323
  }
326
324
  return {
327
- enabled: envFlag("DAILY_FOCUS_JIRA", profile === "work", env),
328
- projects: envList("DAILY_FOCUS_JIRA_PROJECTS", env),
325
+ enabled: flagSetting("DAILY_FOCUS_JIRA", profile === "work", values),
326
+ projects: listSetting("DAILY_FOCUS_JIRA_PROJECTS", values),
329
327
  // Commas only, as for merge gate checks and calendars: status names contain
330
328
  // spaces almost by default — "In Review", "Waiting for customer".
331
- holdStatuses: envNameList("DAILY_FOCUS_JIRA_HOLD_STATUSES", env),
332
- inProgressStatuses: envNameList("DAILY_FOCUS_JIRA_IN_PROGRESS_STATUSES", env),
329
+ holdStatuses: nameListSetting("DAILY_FOCUS_JIRA_HOLD_STATUSES", values),
330
+ inProgressStatuses: nameListSetting("DAILY_FOCUS_JIRA_IN_PROGRESS_STATUSES", values),
333
331
  pollMinutes,
334
332
  // Tolerates a pasted URL, since that is what is on screen when someone goes
335
333
  // looking for their site's name.
336
- site: envString("DAILY_FOCUS_JIRA_SITE", "", env).replace(/^https?:\/\//i, "").replace(/\/.*$/, "") || null,
337
- acliPath: expandHome((env.DAILY_FOCUS_ACLI ?? "").trim() || "acli")
334
+ site: stringSetting("DAILY_FOCUS_JIRA_SITE", "", values).replace(/^https?:\/\//i, "").replace(/\/.*$/, "") || null,
335
+ acliPath: expandHome((values.DAILY_FOCUS_ACLI ?? "").trim() || "acli")
338
336
  };
339
337
  }
340
- function envCli(name, env) {
341
- const raw = envString(name, "off", env).toLowerCase();
338
+ function cliSetting(name, values) {
339
+ const raw = stringSetting(name, "off", values).toLowerCase();
342
340
  if (["off", "false", "0", "no", "none"].includes(raw)) return null;
343
341
  if (!CLI_NAMES.includes(raw)) {
344
342
  throw new Error(`${name} must be off or one of ${CLI_NAMES.join(", ")}, got ${JSON.stringify(raw)}`);
345
343
  }
346
344
  return raw;
347
345
  }
348
- function envAssistant(env) {
349
- const agent = envCli("DAILY_FOCUS_ASSISTANT", env);
350
- const tools = envNameList("DAILY_FOCUS_ASSISTANT_TOOLS", env);
346
+ function assistantConfig(values) {
347
+ const agent = cliSetting("DAILY_FOCUS_ASSISTANT", values);
348
+ const tools = nameListSetting("DAILY_FOCUS_ASSISTANT_TOOLS", values);
351
349
  return {
352
350
  agent,
353
- binPath: expandHome((env.DAILY_FOCUS_ASSISTANT_BIN ?? "").trim() || (agent ?? "claude")),
354
- model: envString("DAILY_FOCUS_ASSISTANT_MODEL", "", env) || null,
355
- effort: envString("DAILY_FOCUS_ASSISTANT_EFFORT", "", env) || null,
351
+ binPath: expandHome((values.DAILY_FOCUS_ASSISTANT_BIN ?? "").trim() || (agent ?? "claude")),
352
+ model: stringSetting("DAILY_FOCUS_ASSISTANT_MODEL", "", values) || null,
353
+ effort: stringSetting("DAILY_FOCUS_ASSISTANT_EFFORT", "", values) || null,
356
354
  tools: tools.length > 0 ? tools : DEFAULT_ASSISTANT_TOOLS,
357
- computerUse: envFlag("DAILY_FOCUS_ASSISTANT_COMPUTER_USE", false, env)
355
+ computerUse: flagSetting("DAILY_FOCUS_ASSISTANT_COMPUTER_USE", false, values),
356
+ auto: flagSetting("DAILY_FOCUS_ASSISTANT_AUTO", false, values),
357
+ autoLimit: boundedSetting("DAILY_FOCUS_ASSISTANT_AUTO_LIMIT", 10, 1, 100, values)
358
358
  };
359
359
  }
360
360
  var DEFAULT_ASSISTANT_TOOLS = ["Bash(gh *)", "Bash(acli *)", "WebFetch", "mcp__claude_ai_Gmail"];
@@ -372,32 +372,33 @@ function briefWritingTools() {
372
372
  ];
373
373
  }
374
374
  var DEFAULT_AGENT_AT = { hour: 7, minute: 0 };
375
- function envAgent(env) {
376
- const cli = envCli("DAILY_FOCUS_AGENT", env);
377
- const at = envTimeOfDay("DAILY_FOCUS_AGENT_AT", env);
378
- if (at && !cli && envString("DAILY_FOCUS_AGENT_AT", "", env) !== "") {
375
+ function agentConfig(values) {
376
+ const cli = cliSetting("DAILY_FOCUS_AGENT", values);
377
+ const at = timeOfDaySetting("DAILY_FOCUS_AGENT_AT", values);
378
+ if (at && !cli && stringSetting("DAILY_FOCUS_AGENT_AT", "", values) !== "") {
379
379
  throw new Error("DAILY_FOCUS_AGENT_AT needs DAILY_FOCUS_AGENT set to codex or claude");
380
380
  }
381
- const tools = envNameList("DAILY_FOCUS_AGENT_TOOLS", env);
381
+ const tools = nameListSetting("DAILY_FOCUS_AGENT_TOOLS", values);
382
382
  return {
383
383
  cli,
384
384
  at: cli ? at : null,
385
- binPath: expandHome((env.DAILY_FOCUS_AGENT_BIN ?? "").trim() || (cli ?? "codex")),
386
- model: envString("DAILY_FOCUS_AGENT_MODEL", "", env) || null,
387
- effort: envString("DAILY_FOCUS_AGENT_EFFORT", "", env) || null,
388
- tools: tools.length > 0 ? tools : DEFAULT_AGENT_TOOLS
385
+ binPath: expandHome((values.DAILY_FOCUS_AGENT_BIN ?? "").trim() || (cli ?? "codex")),
386
+ model: stringSetting("DAILY_FOCUS_AGENT_MODEL", "", values) || null,
387
+ effort: stringSetting("DAILY_FOCUS_AGENT_EFFORT", "", values) || null,
388
+ tools: tools.length > 0 ? tools : DEFAULT_AGENT_TOOLS,
389
+ searchTrash: flagSetting("DAILY_FOCUS_AGENT_SEARCH_TRASH", false, values)
389
390
  };
390
391
  }
391
- function envTimeOfDay(name, env) {
392
- const raw = envString(name, "", env).toLowerCase();
392
+ function timeOfDaySetting(name, values) {
393
+ const raw = stringSetting(name, "", values).toLowerCase();
393
394
  if (raw === "") return DEFAULT_AGENT_AT;
394
395
  if (["off", "false", "0", "no", "none", "never"].includes(raw)) return null;
395
396
  const match = /^([01]?\d|2[0-3]):([0-5]\d)$/.exec(raw);
396
397
  if (!match) throw new Error(`${name} must be a 24-hour time such as 06:30, or off, got ${JSON.stringify(raw)}`);
397
398
  return { hour: Number(match[1]), minute: Number(match[2]) };
398
399
  }
399
- function envWeekdays(name, env) {
400
- const raw = env[name];
400
+ function weekdaysSetting(name, values) {
401
+ const raw = values[name];
401
402
  if (raw === void 0 || raw.trim() === "") return null;
402
403
  try {
403
404
  return parseWeekdays(raw);
@@ -405,28 +406,28 @@ function envWeekdays(name, env) {
405
406
  throw new Error(`${name}: ${error instanceof Error ? error.message : String(error)}`);
406
407
  }
407
408
  }
408
- function envString(name, fallback, env) {
409
- const raw = env[name];
409
+ function stringSetting(name, fallback, values) {
410
+ const raw = values[name];
410
411
  return raw === void 0 || raw.trim() === "" ? fallback : raw.trim();
411
412
  }
412
- function workingDay(env) {
413
- const workStartHour = envBounded("DAILY_FOCUS_WORK_START", 9, 0, 23, env);
414
- const workEndHour = envBounded("DAILY_FOCUS_WORK_END", 17, 1, 24, env);
413
+ function workingDay(values) {
414
+ const workStartHour = boundedSetting("DAILY_FOCUS_WORK_START", 9, 0, 23, values);
415
+ const workEndHour = boundedSetting("DAILY_FOCUS_WORK_END", 17, 1, 24, values);
415
416
  if (workEndHour <= workStartHour) {
416
417
  throw new Error(`DAILY_FOCUS_WORK_END (${workEndHour}) must be later than DAILY_FOCUS_WORK_START (${workStartHour})`);
417
418
  }
418
419
  return { workStartHour, workEndHour };
419
420
  }
420
- function envProfile(env) {
421
- const raw = envString("DAILY_FOCUS_PROFILE", "work", env).toLowerCase();
421
+ function profileSetting(values) {
422
+ const raw = stringSetting("DAILY_FOCUS_PROFILE", "work", values).toLowerCase();
422
423
  if (!PROFILES.includes(raw)) {
423
424
  throw new Error(`DAILY_FOCUS_PROFILE must be one of ${PROFILES.join(", ")}, got ${JSON.stringify(raw)}`);
424
425
  }
425
426
  return raw;
426
427
  }
427
- function loadConfig(env = loadEnv()) {
428
- const dataDir = expandHome(envString("DAILY_FOCUS_DATA", DEFAULT_DATA_DIR, env));
429
- const profile = envProfile(env);
428
+ function loadConfig(values = loadSettings()) {
429
+ const dataDir = expandHome(stringSetting("DAILY_FOCUS_DATA", DEFAULT_DATA_DIR, values));
430
+ const profile = profileSetting(values);
430
431
  return {
431
432
  profile,
432
433
  dataDir,
@@ -444,31 +445,37 @@ function loadConfig(env = loadEnv()) {
444
445
  calendarFile: resolve2(dataDir, "calendar.json"),
445
446
  ticketsFile: resolve2(dataDir, "tickets.json"),
446
447
  timeZoneFile: resolve2(dataDir, "timezone.json"),
447
- port: envBounded("DAILY_FOCUS_PORT", 4321, 0, 65535, env),
448
- host: envString("DAILY_FOCUS_HOST", "127.0.0.1", env),
449
- freeWindows: envFlag("DAILY_FOCUS_FREE_WINDOWS", profile !== "personal", env),
450
- ...workingDay(env),
451
- minFreeWindowMinutes: envBounded("DAILY_FOCUS_MIN_FREE_WINDOW", 45, 1, 24 * 60, env),
452
- staleAfterHours: envBounded("DAILY_FOCUS_STALE_AFTER_HOURS", 24, 1, 24 * 30, env),
453
- agentDays: envWeekdays("DAILY_FOCUS_AGENT_DAYS", env),
454
- github: envGitHub(env),
455
- calendar: envCalendar(env),
456
- jira: envJira(env, profile),
457
- assistant: envAssistant(env),
448
+ port: boundedSetting("DAILY_FOCUS_PORT", 4321, 0, 65535, values),
449
+ host: stringSetting("DAILY_FOCUS_HOST", "127.0.0.1", values),
450
+ freeWindows: flagSetting("DAILY_FOCUS_FREE_WINDOWS", profile !== "personal", values),
451
+ ...workingDay(values),
452
+ minFreeWindowMinutes: boundedSetting("DAILY_FOCUS_MIN_FREE_WINDOW", 45, 1, 24 * 60, values),
453
+ staleAfterHours: boundedSetting("DAILY_FOCUS_STALE_AFTER_HOURS", 24, 1, 24 * 30, values),
454
+ agentDays: weekdaysSetting("DAILY_FOCUS_AGENT_DAYS", values),
455
+ github: gitHubConfig(values),
456
+ calendar: calendarConfig(values),
457
+ jira: jiraConfig(values, profile),
458
+ assistant: assistantConfig(values),
458
459
  assistantLogFile: resolve2(dataDir, "assistant.jsonl"),
459
460
  assistantDir: resolve2(dataDir, "assistant"),
460
461
  assistantPromptFile: resolve2(dataDir, "assistant.md"),
461
462
  assistantPromptSource: resolve2(import.meta.dirname, "..", "prompts", "assistant.md"),
462
- agent: envAgent(env),
463
- agentLogFile: resolve2(dataDir, "agent.jsonl")
463
+ agent: agentConfig(values),
464
+ agentLogFile: resolve2(dataDir, "agent.jsonl"),
465
+ agentSettingsFile: resolve2(dataDir, "agent-settings.json"),
466
+ onboardingFile: resolve2(dataDir, "onboarding.json"),
467
+ onboardingDir: resolve2(dataDir, "onboarding"),
468
+ onboardingPromptSource: resolve2(import.meta.dirname, "..", "prompts", "onboarding.md")
464
469
  };
465
470
  }
466
471
 
467
472
  export {
473
+ SETTINGS_FILE,
468
474
  LAUNCH_SETTINGS,
469
- readDotEnv,
470
- envSources,
471
- layerEnv,
475
+ expandHome,
476
+ settingSources,
477
+ layerSettings,
478
+ oldDotenvWarning,
472
479
  localTimeZone,
473
480
  localDateKey,
474
481
  isDateOnly,
@@ -1,3 +1,6 @@
1
+ import {
2
+ writeJsonAtomic
3
+ } from "./chunk-QG4XXKBP.js";
1
4
  import {
2
5
  BRIEF_REFRESH_GRACE_HOURS,
3
6
  calendarDaysBetween,
@@ -14,21 +17,7 @@ import {
14
17
  startOfLocalDay,
15
18
  workingDaysSince,
16
19
  workingMsBetween
17
- } from "./chunk-KZNY45XW.js";
18
-
19
- // src/fs.ts
20
- import { mkdir, rename, writeFile } from "node:fs/promises";
21
- import { dirname } from "node:path";
22
- async function writeJsonAtomic(path, value) {
23
- await mkdir(dirname(path), { recursive: true });
24
- const temp = tempPathFor(path);
25
- await writeFile(temp, `${JSON.stringify(value, null, 2)}
26
- `, "utf8");
27
- await rename(temp, path);
28
- }
29
- function tempPathFor(path) {
30
- return `${path}.tmp`;
31
- }
20
+ } from "./chunk-72NUDSOV.js";
32
21
 
33
22
  // src/ids.ts
34
23
  function canonicalId(id) {
@@ -392,7 +381,7 @@ function findLeakedPhrase(brief, focus) {
392
381
 
393
382
  // src/store.ts
394
383
  import { createHash } from "node:crypto";
395
- import { appendFile, mkdir as mkdir2, readFile as readFile2, stat } from "node:fs/promises";
384
+ import { appendFile, mkdir, readFile as readFile2, stat } from "node:fs/promises";
396
385
  import { setTimeout as delay } from "node:timers/promises";
397
386
 
398
387
  // src/agenda.ts
@@ -549,7 +538,7 @@ var Store = class {
549
538
  this.config = config;
550
539
  }
551
540
  async ensureDataDir() {
552
- await mkdir2(this.config.dataDir, { recursive: true });
541
+ await mkdir(this.config.dataDir, { recursive: true });
553
542
  }
554
543
  /**
555
544
  * Which store this is, as an opaque fingerprint: its path, and the directory's
@@ -824,8 +813,6 @@ function resolveItems(items, actions, now) {
824
813
  }
825
814
 
826
815
  export {
827
- writeJsonAtomic,
828
- tempPathFor,
829
816
  canonicalId,
830
817
  fingerprintId,
831
818
  parseBrief,