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.
- package/README.md +18 -4
- package/SETUP.md +156 -105
- package/dist/{audit-DNTFZOHH.js → audit-AQUBVWPA.js} +4 -3
- package/dist/{chunk-KZNY45XW.js → chunk-72NUDSOV.js} +126 -119
- package/dist/{chunk-GQ3NCWRT.js → chunk-ITUK7XKS.js} +6 -19
- package/dist/{chunk-KL6RU4XT.js → chunk-JTMBBUMD.js} +316 -10
- package/dist/chunk-QG4XXKBP.js +18 -0
- package/dist/chunk-U6EPOXEW.js +570 -0
- package/dist/{chunk-P3E2B2SD.js → chunk-WPXIQPYX.js} +1 -1
- package/dist/{chunk-6SMUSPZG.js → chunk-YPGKXTBX.js} +2 -2
- package/dist/cli.js +19 -12
- package/dist/{demo-TIIXODJG.js → demo-KE6WDWMA.js} +159 -37
- package/dist/{init-XDMAXU2S.js → init-5KJVHWJU.js} +41 -6
- package/dist/{seed-TWT7EDXJ.js → seed-T4VEY6DT.js} +4 -3
- package/dist/{server-WT4CQ3VX.js → server-FFRNYEAO.js} +2000 -743
- package/dist/{service-ZUHI3VKH.js → service-LQ46YRCZ.js} +7 -6
- package/package.json +2 -2
- package/prompts/README.md +20 -4
- package/prompts/assistant.md +7 -1
- package/prompts/morning-brief-personal.md +37 -0
- package/prompts/morning-brief-work.md +37 -0
- package/prompts/onboarding.md +41 -0
- package/public/app.js +740 -279
- package/public/style.css +671 -75
- package/dist/chunk-V2UN3JYL.js +0 -140
|
@@ -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
|
|
64
|
-
const
|
|
65
|
-
for (const
|
|
66
|
-
|
|
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
|
-
|
|
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 {
|
|
57
|
+
return { launch, settings: values, fixed, settingsPath, settingsError: error };
|
|
79
58
|
}
|
|
80
|
-
function
|
|
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(
|
|
86
|
-
if (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
|
-
|
|
91
|
-
|
|
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
|
|
244
|
-
const raw =
|
|
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
|
|
253
|
-
const n =
|
|
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(
|
|
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
|
|
260
|
-
const raw =
|
|
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
|
|
270
|
-
const raw =
|
|
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
|
|
285
|
-
const flag = (
|
|
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
|
|
290
|
-
const pollMinutes =
|
|
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:
|
|
296
|
-
accounts:
|
|
297
|
-
scope: parseScope(
|
|
298
|
-
mergeGateChecks:
|
|
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((
|
|
298
|
+
ghPath: expandHome((values.DAILY_FOCUS_GH ?? "").trim() || "gh")
|
|
301
299
|
};
|
|
302
300
|
}
|
|
303
|
-
function
|
|
304
|
-
const pollMinutes =
|
|
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:
|
|
307
|
+
enabled: flagSetting("DAILY_FOCUS_CALENDAR", true, values),
|
|
310
308
|
// Commas only, as for merge gate checks: calendar names contain spaces.
|
|
311
|
-
names:
|
|
312
|
-
addresses:
|
|
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
|
-
(
|
|
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
|
|
322
|
-
const pollMinutes =
|
|
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:
|
|
328
|
-
projects:
|
|
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:
|
|
332
|
-
inProgressStatuses:
|
|
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:
|
|
337
|
-
acliPath: expandHome((
|
|
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
|
|
341
|
-
const raw =
|
|
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
|
|
349
|
-
const agent =
|
|
350
|
-
const tools =
|
|
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((
|
|
354
|
-
model:
|
|
355
|
-
effort:
|
|
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:
|
|
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
|
|
376
|
-
const cli =
|
|
377
|
-
const at =
|
|
378
|
-
if (at && !cli &&
|
|
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 =
|
|
381
|
+
const tools = nameListSetting("DAILY_FOCUS_AGENT_TOOLS", values);
|
|
382
382
|
return {
|
|
383
383
|
cli,
|
|
384
384
|
at: cli ? at : null,
|
|
385
|
-
binPath: expandHome((
|
|
386
|
-
model:
|
|
387
|
-
effort:
|
|
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
|
|
392
|
-
const raw =
|
|
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
|
|
400
|
-
const raw =
|
|
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
|
|
409
|
-
const raw =
|
|
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(
|
|
413
|
-
const workStartHour =
|
|
414
|
-
const workEndHour =
|
|
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
|
|
421
|
-
const raw =
|
|
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(
|
|
428
|
-
const dataDir = expandHome(
|
|
429
|
-
const profile =
|
|
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:
|
|
448
|
-
host:
|
|
449
|
-
freeWindows:
|
|
450
|
-
...workingDay(
|
|
451
|
-
minFreeWindowMinutes:
|
|
452
|
-
staleAfterHours:
|
|
453
|
-
agentDays:
|
|
454
|
-
github:
|
|
455
|
-
calendar:
|
|
456
|
-
jira:
|
|
457
|
-
assistant:
|
|
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:
|
|
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
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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-
|
|
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
|
|
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
|
|
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,
|