daily-focus 1.2.0-dev.10 → 1.2.0-dev.11

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
@@ -35,9 +35,11 @@ To use your own data:
35
35
  npx daily-focus
36
36
  ```
37
37
 
38
- Until the first brief exists, the Today tab walks you through the rest: what it
39
- briefs, your objective, which coding-agent CLI writes the brief (Claude Code or
40
- Codex), and where it should look. Everything stays on the settings page afterwards.
38
+ A new store opens on the setup screen: a few questions, one at a time, each with
39
+ the likely answer already ticked. Once you've chosen the coding-agent CLI that writes
40
+ the brief (Claude Code or Codex), it looks into the rest for you, such as which of
41
+ your calendars are work ones, so most answers are a click. Everything stays on the
42
+ settings page afterwards.
41
43
  To keep it running in the background on macOS, install it and run it as a service:
42
44
 
43
45
  ```sh
package/SETUP.md CHANGED
@@ -27,18 +27,56 @@ and `npm start` runs it in the terminal. Each `daily-focus <command>` below is
27
27
  `npm run <command>` in a checkout, with two differences: `build-calendar` is
28
28
  `build:calendar`, and flags go after `--`, as in `npm run audit -- --against <file>`.
29
29
 
30
- Until the first brief exists, the Today tab walks you through setting it up:
31
-
32
- 1. **What it briefs**: your work, or your personal life. See
30
+ A new store opens on the setup screen. It first looks for Codex and Claude Code:
31
+ on your login shell's PATH, then where their installers put them, and checks that
32
+ the one it finds is logged in. With one ready it doesn't ask: it uses Codex if
33
+ that is ready, otherwise Claude Code, and says so in a line you can **Change**.
34
+ With neither ready, it says what is wrong with each and the command that fixes it
35
+ (`codex login`, `claude auth login`), links to the installers, and takes the path
36
+ to one it couldn't find.
37
+
38
+ Then it asks a few questions, one at a time, and each comes with the answer you
39
+ most likely want already ticked: confirm it or change it. A list can be left
40
+ unticked and a field empty. Two questions can be left, by a button that says what
41
+ that does: **Decide later** for the agent, and **Leave this out** for who you are.
42
+ **Change** reopens an earlier answer.
43
+
44
+ 1. **Who writes the brief**, only when it couldn't pick for you.
45
+ 2. **What it briefs**: your work, or your personal life. See
33
46
  [A personal instance](#a-personal-instance) for running both.
34
- 2. **Your objective**: the one thing you're trying to achieve, which the agent
35
- ranks the day against.
36
- 3. **Who writes the brief**: Claude Code or Codex, whichever it finds installed.
37
- 4. **Where it should look**: your source list, which starts from a template with
38
- what `gh` and `git` know about you filled in.
39
- 5. **What else to show**: the pull request board, the Jira board and the live
40
- agenda, each with its status and a button to turn it off.
41
- 6. **Write my first brief**, which runs the agent once while you watch.
47
+ 3. **When it runs**, and on which days.
48
+ 4. **Who you are**: your name as meeting notes write it, your email, and each
49
+ GitHub account `gh` is logged in to, with what it's for.
50
+ 5. **What else to show**: the pull request board and the Jira board, each with
51
+ its status.
52
+ 6. **Which calendars** the brief reads, which the live agenda follows too.
53
+ 7. **Your objective**, which the agent ranks the day against, and what's in the
54
+ way. It suggests a few from what you've been doing, when something stands
55
+ out, and writes one up from whatever you tell it, however rough.
56
+
57
+ The [assistant](#ask-the-assistant) isn't asked about: it comes on with the CLI
58
+ that writes the brief, unless you've set it already.
59
+
60
+ Once you've said what it briefs, the CLI looks into the others in the background,
61
+ one short session each, so the slow ones are ready when you get to them. It reads
62
+ `gh`, your mail and your calendars to tell work from personal, and says in a
63
+ sentence why it ticked what it did. Reply under that sentence with what to change,
64
+ such as "also the on-call rota", and it tries again, answering in the same place.
65
+ Continue before it has finished and it asks first, then stops and keeps what the
66
+ card holds.
67
+
68
+ The sessions run on the newest flagship model at low effort (Opus for Claude Code,
69
+ the first model in Codex's own list for Codex), which measured as fast as the
70
+ lighter models and, unlike them, told your calendars from other people's. They
71
+ only read, in an empty directory. Claude Code is given read tools by name, and
72
+ under Codex every connector write is refused, drafts included; `gh` and `acli` are
73
+ held to reading by their instructions, as
74
+ [the assistant's](#what-the-assistant-can-do) are.
75
+
76
+ Nothing is saved until the last step, **Write my first brief**. It saves the
77
+ settings, writes your source list from the answers (unless you already have one,
78
+ which it leaves alone) and your objective, restarts the dashboard to apply them,
79
+ and runs the agent once while you watch.
42
80
 
43
81
  Everything in it stays on the settings page afterwards: the gear in the header, or
44
82
  <kbd>,</kbd>. The dashboard keeps its files in `~/.daily-focus/`, and links the
@@ -52,6 +90,8 @@ terminal open while you use the dashboard. `daily-focus --help` lists the rest.
52
90
 
53
91
  If you'd rather write the files by hand, `daily-focus init` creates the store with
54
92
  templates for both. It preserves existing files, so you can safely run it again.
93
+ A setting given in the environment, such as `DAILY_FOCUS_PROFILE`, is shown on the
94
+ setup screen as answered and isn't asked.
55
95
 
56
96
  ## Set your objective and sources
57
97
 
@@ -91,7 +131,7 @@ Keep real names, account handles, and work details in this private store, outsid
91
131
 
92
132
  The dashboard runs the agent through a coding-agent CLI you already have. Install
93
133
  and log in to the [Codex CLI](https://developers.openai.com/codex/cli) or
94
- [Claude Code](https://code.claude.com/docs), and choose it in the setup steps or
134
+ [Claude Code](https://code.claude.com/docs), and choose it on the setup screen or
95
135
  under **Morning agent** in the settings. In `.env` or the environment that is:
96
136
 
97
137
  ```dotenv
@@ -108,7 +148,8 @@ half minute. The days are Monday to Friday unless `DAILY_FOCUS_AGENT_DAYS` says
108
148
  otherwise; see [Match your working week](#match-your-working-week).
109
149
  `DAILY_FOCUS_AGENT_AT=off` means the dashboard never starts a run on its own; the
110
150
  refresh icon still does. A new store's first brief always comes from the setup
111
- steps' button, so the clock never runs the agent before you've told it where to look.
151
+ screen's last button, so the clock never runs the agent before you've told it where
152
+ to look.
112
153
 
113
154
  "Your time" is the time zone of the browser you last looked at the dashboard
114
155
  in. The page tells the dashboard when it loads, reconnects or comes back into view,
@@ -470,8 +511,8 @@ daily-focus service --remove
470
511
 
471
512
  Daily Focus can brief your personal life as well as your work: email, family
472
513
  calendars, Apple Reminders, Apple Messages, e-Boks and your own GitHub projects. Run it as a second instance
473
- with its own store, server and morning agent, and choose **Personal life** in its
474
- setup steps, or set:
514
+ with its own store, server and morning agent, and choose **Personal life** on its
515
+ setup screen, or set:
475
516
 
476
517
  ```dotenv
477
518
  DAILY_FOCUS_PROFILE=personal
@@ -580,9 +621,10 @@ matters when `DAILY_FOCUS_FREE_WINDOWS` is off.
580
621
  which it ends with. Run `daily-focus service` to keep it running. If the
581
622
  service is installed and the page still doesn't load, read
582
623
  `~/Library/Logs/local.daily-focus.log`.
583
- - Before the first brief, the Today tab shows the setup steps. Once a brief has
584
- existed, an empty Today tab means the agent hasn't written a new `items.json`:
585
- start a run from the refresh icon beside the brief's age.
624
+ - After setup and before the first brief, the Today tab says where the first brief
625
+ is, and offers to run it again if it didn't finish. Once a brief has existed, an
626
+ empty Today tab means the agent hasn't written a new `items.json`: start a run
627
+ from the refresh icon beside the brief's age.
586
628
  - A stale brief needs an agent run. Refreshing a board doesn't regenerate the brief;
587
629
  the icon beside the brief's age does. Click "updated … ago" to see what the last
588
630
  run said, or whether there was one: the dashboard has to be running at
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  readSystemZone,
3
3
  startingZone
4
- } from "./chunk-Z4SFMMRQ.js";
4
+ } from "./chunk-GVLNDQVQ.js";
5
5
  import {
6
6
  linkState
7
7
  } from "./chunk-QKT2FAB5.js";
@@ -13,7 +13,7 @@ import {
13
13
  resolveItems,
14
14
  runContractChecks,
15
15
  setsStatus
16
- } from "./chunk-RZNQJ6UG.js";
16
+ } from "./chunk-CPJ6LOVC.js";
17
17
  import {
18
18
  BRIEF_REFRESH_GRACE_HOURS,
19
19
  describeSchedule,
@@ -23,7 +23,7 @@ import {
23
23
  resolveSchedule,
24
24
  runsOn,
25
25
  workingMsBetween
26
- } from "./chunk-J7ZYVUG6.js";
26
+ } from "./chunk-4DZEBUPU.js";
27
27
  import {
28
28
  command
29
29
  } from "./chunk-BC2W7NND.js";
@@ -462,12 +462,16 @@ function loadConfig(env = loadEnv()) {
462
462
  assistantPromptFile: resolve2(dataDir, "assistant.md"),
463
463
  assistantPromptSource: resolve2(import.meta.dirname, "..", "prompts", "assistant.md"),
464
464
  agent: envAgent(env),
465
- agentLogFile: resolve2(dataDir, "agent.jsonl")
465
+ agentLogFile: resolve2(dataDir, "agent.jsonl"),
466
+ onboardingFile: resolve2(dataDir, "onboarding.json"),
467
+ onboardingDir: resolve2(dataDir, "onboarding"),
468
+ onboardingPromptSource: resolve2(import.meta.dirname, "..", "prompts", "onboarding.md")
466
469
  };
467
470
  }
468
471
 
469
472
  export {
470
473
  LAUNCH_SETTINGS,
474
+ expandHome,
471
475
  readDotEnv,
472
476
  envSources,
473
477
  layerEnv,
@@ -14,7 +14,7 @@ import {
14
14
  startOfLocalDay,
15
15
  workingDaysSince,
16
16
  workingMsBetween
17
- } from "./chunk-J7ZYVUG6.js";
17
+ } from "./chunk-4DZEBUPU.js";
18
18
 
19
19
  // src/ids.ts
20
20
  function canonicalId(id) {
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  canonicalId,
3
3
  foldActionLog
4
- } from "./chunk-RZNQJ6UG.js";
4
+ } from "./chunk-CPJ6LOVC.js";
5
5
  import {
6
6
  laterISO,
7
7
  localDateKey,
8
8
  parseISO
9
- } from "./chunk-J7ZYVUG6.js";
9
+ } from "./chunk-4DZEBUPU.js";
10
10
 
11
11
  // src/codex.ts
12
12
  import { execFile } from "node:child_process";
@@ -109,6 +109,18 @@ async function codexVersionWarning(bin, run = defaultExec) {
109
109
  const consequence = olderThan(version, CODEX_CONNECTOR_POLICY) ? `Before ${CODEX_CONNECTOR_POLICY} Codex ignores the dashboard's refusal of connector writes without saying so, so the dashboard won't start the agent or the assistant on it.` : `Before ${CODEX_MINIMUM} Codex can't switch computer use off, so the assistant won't answer while computer use is off in its settings.`;
110
110
  return `Codex ${version} is older than ${CODEX_MINIMUM}, the oldest the dashboard's limits on Codex work with. ${consequence} Update Codex.`;
111
111
  }
112
+ async function codexLatestModel(bin, run = defaultExec) {
113
+ try {
114
+ const catalog = JSON.parse(await run(bin, ["debug", "models"], { timeout: LIST_TIMEOUT_MS }));
115
+ const listed = (Array.isArray(catalog.models) ? catalog.models : []).filter(
116
+ (model) => typeof model === "object" && model !== null && typeof model.slug === "string" && typeof model.priority === "number" && model.visibility === "list"
117
+ );
118
+ listed.sort((a, b) => a.priority - b.priority);
119
+ return listed[0]?.slug ?? null;
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
112
124
 
113
125
  // src/assistant.ts
114
126
  import { spawn } from "node:child_process";
@@ -263,7 +275,7 @@ function parseLine(line) {
263
275
  }
264
276
  }
265
277
  var claudeAdapter = {
266
- args({ prompt, sessionId, model, effort, tools, denyEdits }) {
278
+ args({ prompt, sessionId, model, effort, tools, denyEdits, schema }) {
267
279
  const args = [
268
280
  "-p",
269
281
  "--output-format",
@@ -278,6 +290,7 @@ var claudeAdapter = {
278
290
  if (tools.length > 0) args.push("--allowedTools", ...tools);
279
291
  if (model) args.push("--model", model);
280
292
  if (effort) args.push("--effort", effort);
293
+ if (schema) args.push("--json-schema", schema.json);
281
294
  if (sessionId) args.push("--resume", sessionId);
282
295
  args.push("--", prompt);
283
296
  return args;
@@ -310,10 +323,11 @@ var claudeAdapter = {
310
323
  }
311
324
  };
312
325
  var codexAdapter = {
313
- args({ prompt, sessionId, model, effort, drafts, codexConfig }) {
326
+ args({ prompt, sessionId, model, effort, drafts, codexConfig, schema }) {
314
327
  const args = ["exec"];
315
328
  if (sessionId) args.push("resume");
316
329
  args.push("--json", "--skip-git-repo-check");
330
+ if (schema) args.push("--output-schema", schema.file);
317
331
  const config = [
318
332
  'sandbox_mode="workspace-write"',
319
333
  "sandbox_workspace_write.network_access=true",
@@ -354,7 +368,7 @@ var ADAPTERS = {
354
368
  claude: claudeAdapter,
355
369
  codex: codexAdapter
356
370
  };
357
- var CLAUDE_QUIET_TOOLS = /* @__PURE__ */ new Set(["ToolSearch", "TodoWrite"]);
371
+ var CLAUDE_QUIET_TOOLS = /* @__PURE__ */ new Set(["ToolSearch", "TodoWrite", "StructuredOutput"]);
358
372
  function claudeStep(block) {
359
373
  if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
360
374
  return { id: block.tool_use_id, state: block.is_error === true ? "failed" : "done" };
@@ -989,8 +1003,11 @@ function countBoard(rows) {
989
1003
  }
990
1004
 
991
1005
  export {
1006
+ computerUseOff,
1007
+ refusalFor,
992
1008
  CodexGate,
993
1009
  codexVersionWarning,
1010
+ codexLatestModel,
994
1011
  QUICK_ACTIONS,
995
1012
  ADAPTERS,
996
1013
  Assistant,
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  writeJsonAtomic
3
- } from "./chunk-RZNQJ6UG.js";
3
+ } from "./chunk-CPJ6LOVC.js";
4
4
  import {
5
5
  localTimeZone
6
- } from "./chunk-J7ZYVUG6.js";
6
+ } from "./chunk-4DZEBUPU.js";
7
7
 
8
8
  // src/zone.ts
9
9
  import { readFile, readlink } from "node:fs/promises";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  localDateKey
3
- } from "./chunk-J7ZYVUG6.js";
3
+ } from "./chunk-4DZEBUPU.js";
4
4
 
5
5
  // src/sample.ts
6
6
  function timeOn(now, hour, minute = 0) {
package/dist/cli.js CHANGED
@@ -49,7 +49,7 @@ function shouldOpen(flag) {
49
49
  return flag && process.stdout.isTTY === true && !process.env.CI;
50
50
  }
51
51
  async function demo(open, profile) {
52
- const { demoEnv, demoIntegrations, demoProfile, writeDemoStore } = await import("./demo-BYLLMKSQ.js");
52
+ const { demoEnv, demoIntegrations, demoProfile, writeDemoStore } = await import("./demo-UMGG3VKY.js");
53
53
  const chosen = demoProfile(profile);
54
54
  const store = await mkdtemp(join(tmpdir(), "daily-focus-demo-"));
55
55
  process.on("exit", () => rmSync(store, { recursive: true, force: true }));
@@ -66,7 +66,7 @@ async function demo(open, profile) {
66
66
  );
67
67
  }
68
68
  async function start(open, onStarted, options = {}) {
69
- const { runServer } = await import("./server-WQYUOKB3.js");
69
+ const { runServer } = await import("./server-EDPYEZCP.js");
70
70
  await runServer({
71
71
  ...options,
72
72
  onStarted: (url) => {
@@ -163,20 +163,20 @@ async function main(argv) {
163
163
  }
164
164
  return start(open);
165
165
  case "service": {
166
- const { main: service } = await import("./service-PUJ3YDH5.js");
166
+ const { main: service } = await import("./service-XPFMYIHR.js");
167
167
  process.exitCode = await service(values.remove ? ["--remove"] : []);
168
168
  return;
169
169
  }
170
170
  // These three are scripts that do their work as they load, and read their
171
171
  // own flags from the command line.
172
172
  case "init":
173
- await import("./init-EMZDG2I7.js");
173
+ await import("./init-MFPXASX3.js");
174
174
  return;
175
175
  case "audit":
176
- await import("./audit-7ZCLKF46.js");
176
+ await import("./audit-F26VGPOO.js");
177
177
  return;
178
178
  case "seed":
179
- await import("./seed-JU4LQ33D.js");
179
+ await import("./seed-S32JX5MW.js");
180
180
  return;
181
181
  case "build-calendar":
182
182
  return buildCalendar();
@@ -3,17 +3,17 @@ import {
3
3
  actionOf,
4
4
  autoKeys,
5
5
  judge
6
- } from "./chunk-ODJGQCM3.js";
6
+ } from "./chunk-EV44BC2F.js";
7
7
  import {
8
8
  dateFromNow,
9
9
  sampleBrief,
10
10
  sampleGeneratedAt
11
- } from "./chunk-64XB7UDN.js";
12
- import "./chunk-RZNQJ6UG.js";
11
+ } from "./chunk-HVCCIZGA.js";
12
+ import "./chunk-CPJ6LOVC.js";
13
13
  import {
14
14
  PROFILES,
15
15
  localDateKey
16
- } from "./chunk-J7ZYVUG6.js";
16
+ } from "./chunk-4DZEBUPU.js";
17
17
  import "./chunk-BC2W7NND.js";
18
18
 
19
19
  // src/demo.ts
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-QKT2FAB5.js";
9
9
  import {
10
10
  loadConfig
11
- } from "./chunk-J7ZYVUG6.js";
11
+ } from "./chunk-4DZEBUPU.js";
12
12
  import {
13
13
  PACKAGED,
14
14
  command
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  sampleBrief
3
- } from "./chunk-64XB7UDN.js";
3
+ } from "./chunk-HVCCIZGA.js";
4
4
  import {
5
5
  Store
6
- } from "./chunk-RZNQJ6UG.js";
6
+ } from "./chunk-CPJ6LOVC.js";
7
7
  import {
8
8
  loadConfig
9
- } from "./chunk-J7ZYVUG6.js";
9
+ } from "./chunk-4DZEBUPU.js";
10
10
  import {
11
11
  command
12
12
  } from "./chunk-BC2W7NND.js";