claude-home 1.5.26 → 1.5.31

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
@@ -2,28 +2,53 @@
2
2
 
3
3
  A local web dashboard for [Claude Code](https://claude.ai/code) power users.
4
4
 
5
- Browse your session history, manage skills, agents, hooks, commands, plans, and discover community skills — all from a clean UI. No tokens consumed. Runs entirely on your machine.
5
+ Browse your session history, manage skills, agents, hooks, commands, plans, and notes — all from a clean UI. No tokens consumed. Runs entirely on your machine.
6
6
 
7
7
  ---
8
8
 
9
9
  ## Why claude-home?
10
10
 
11
- Claude Code is powerful but headless. Everything lives in files scattered across `~/.claude/` and your project directories. claude-home gives you a visual interface to explore and manage all of it:
11
+ Claude Code is powerful but headless. Everything lives in files scattered across `~/.claude/` and your project directories. claude-home gives you a visual interface to explore and manage all of it — and a **second brain** layer that persists what you build with Claude day to day.
12
12
 
13
- - **Session history** — browse every conversation you've had with Claude, search by content, resume from where you left off
14
- - **Skills** — view, create, edit and delete your user and project skills. Discover and install new ones from the community marketplace or any GitHub URL
15
- - **Agents** — manage your custom agent definitions with full CRUD
16
- - **Instructions** — read and edit your `CLAUDE.md` files (global and per-project), the instructions that shape Claude's behavior in every session
17
- - **Permissions** — inspect the allow/deny lists that control what Claude can do in each project
18
- - **Hooks** view and manage all your configured hooks across global and project settings
19
- - **Commands** — browse your slash command library
20
- - **Plans** — read and search your GSD/planning files
21
- - **Memory** inspect your auto-memory entries across projects
22
- - **Configuration** — view Claude settings per project
13
+ ---
14
+
15
+ ## Features
16
+
17
+ ### Sessions
18
+ Browse every conversation you've had with Claude. Search by content, filter by project or branch, resume from where you left off, export as Markdown, or publish as a public GitHub Gist. Direct shareable links (`#/session/...`) let you bookmark or share specific conversations.
19
+
20
+ ### Today
21
+ A daily task list that lives alongside your Claude work. Add tasks, provide context (hours available, meetings, energy), and hit **Copy for Claude** to get a formatted prompt ready to paste for prioritization. Uncompleted tasks carry over automatically to the next day.
22
+
23
+ Claude can add tasks directly from any session — just say "add this to today" or "remind me tomorrow to…"
24
+
25
+ ### Notes
26
+ Your personal notepad — separate from Claude's memory. Notes are for you, not for Claude's context. Capture decisions, TILs, bug solutions, runbooks, snippets, or anything worth keeping.
27
+
28
+ Claude can save notes directly from any session — just say "save this as a note". One-click setup from **Config → Integrations** adds the instruction to your `CLAUDE.md` automatically and grants the necessary write permissions.
29
+
30
+ Direct links (`#/note/filename`) let you open a specific note instantly.
31
+
32
+ ### Projects
33
+ Visual overview of all your Claude projects with session count, token usage, cost, and memory files. Drill into any project to browse its sessions, memory entries, and `CLAUDE.md` files.
34
+
35
+ ### Plans
36
+ Read and search your GSD/planning files from `~/.claude/plans/`. Export or publish as a Gist with one click.
37
+
38
+ ### Memory
39
+ Inspect your auto-memory entries across all projects.
40
+
41
+ ### Skills & Agents
42
+ View, create, edit and delete your user and project skills and custom agent definitions. Discover and install new skills from the community marketplace or any GitHub URL.
23
43
 
24
- ### No tokens. No cloud. No tracking.
44
+ ### Instructions & Config
45
+ Read and edit your `CLAUDE.md` files (global and per-project). Inspect permissions, hooks, and Claude settings per project.
25
46
 
26
- claude-home is a local Express server that reads your `~/.claude/` directory directly. It never calls the Claude API, never sends data anywhere, and works completely offline (except for the marketplace feature, which optionally fetches from GitHub).
47
+ ---
48
+
49
+ ## No tokens. No cloud. No tracking.
50
+
51
+ claude-home is a local Express server that reads your `~/.claude/` directory directly. It never calls the Claude API, never sends data anywhere, and works completely offline (except for the marketplace and Gist sharing features).
27
52
 
28
53
  ---
29
54
 
@@ -91,6 +116,23 @@ Commands:
91
116
 
92
117
  ---
93
118
 
119
+ ## Claude integration (Notes & Today)
120
+
121
+ claude-home can receive content directly from Claude during active sessions. Go to **Config → Integrations** and click **Set up** — this adds instructions to your `~/.claude/CLAUDE.md` and grants the necessary write permissions automatically.
122
+
123
+ Once set up, from any Claude session you can say:
124
+ - *"Save this as a note"* → creates a note in the Notes view
125
+ - *"Add this to today's tasks"* → adds a task to the Today view
126
+ - *"Remind me tomorrow to…"* → adds a task to tomorrow's list
127
+
128
+ ---
129
+
130
+ ## Sharing
131
+
132
+ Publish sessions or plans as public GitHub Gists. Go to **Config → Sharing**, add a GitHub personal access token with the `gist` scope, and use the **Gist** button in any session or plan.
133
+
134
+ ---
135
+
94
136
  ## Skills Marketplace
95
137
 
96
138
  claude-home includes a marketplace to discover and install skills from GitHub repositories.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-home",
3
- "version": "1.5.26",
3
+ "version": "1.5.31",
4
4
  "description": "Web dashboard for Claude Code — browse sessions, manage skills, hooks, commands, and agents",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -1519,6 +1519,18 @@
1519
1519
  </div>
1520
1520
 
1521
1521
  <div class="nav-section">
1522
+ <div class="nav-item" :class="{ active: view === 'today' }" @click="view='today';selectedSession=null;loadToday()">
1523
+ <span class="nav-icon">
1524
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
1525
+ <rect x="2" y="2" width="12" height="12" rx="1"/>
1526
+ <line x1="5" y1="2" x2="5" y2="5"/><line x1="11" y1="2" x2="11" y2="5"/>
1527
+ <line x1="2" y1="7" x2="14" y2="7"/>
1528
+ <text x="5" y="13" font-size="5" fill="currentColor" stroke="none" font-weight="bold" x-text="new Date().getDate()"></text>
1529
+ </svg>
1530
+ </span>
1531
+ Today
1532
+ <span class="nav-count" x-show="todayData && todayData.tasks.filter(t=>!t.done).length > 0" x-text="todayData ? todayData.tasks.filter(t=>!t.done).length : ''"></span>
1533
+ </div>
1522
1534
  <div class="nav-item" :class="{ active: view === 'dashboard' }" @click="view='dashboard';selectedSession=null;loadStats();loadInsights()">
1523
1535
  <span class="nav-icon">
1524
1536
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke-width="1.5">
@@ -2534,6 +2546,84 @@
2534
2546
  </div>
2535
2547
  </template>
2536
2548
 
2549
+ <!-- Today view -->
2550
+ <template x-if="view === 'today' && !selectedSession">
2551
+ <div style="display:flex;flex-direction:column;height:100%;overflow:hidden;">
2552
+ <div class="topbar">
2553
+ <span class="topbar-title">Today</span>
2554
+ <span style="font-size:11px;color:var(--ink-3)" x-text="new Date().toLocaleDateString('en-US',{weekday:'long',month:'long',day:'numeric'})"></span>
2555
+ <div style="margin-left:auto;display:flex;gap:8px;align-items:center">
2556
+ <span x-show="todayCopied" style="font-size:12px;color:var(--green)" x-transition>Copied!</span>
2557
+ <button class="btn btn-sm" style="background:var(--canvas-2);color:var(--ink);border:1px solid var(--rule)" @click="copyTodayForClaude()" title="Copy tasks + context formatted for Claude">Copy for Claude</button>
2558
+ </div>
2559
+ </div>
2560
+ <div style="flex:1;overflow-y:auto;padding:24px;max-width:680px;width:100%">
2561
+ <template x-if="todayLoading"><div class="loading"><div class="spinner"></div> Loading…</div></template>
2562
+ <template x-if="!todayLoading && todayData">
2563
+ <div style="display:flex;flex-direction:column;gap:20px">
2564
+
2565
+ <!-- Context -->
2566
+ <div>
2567
+ <div style="font-size:11px;font-weight:600;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Context for today</div>
2568
+ <textarea x-model="todayData.context" @input="saveToday()" placeholder="Hours available, meetings, energy level, deadlines…" rows="2" style="width:100%;background:var(--canvas-2);border:1px solid var(--rule);border-radius:6px;padding:8px 10px;font-size:12px;color:var(--ink);font-family:inherit;resize:vertical;box-sizing:border-box"></textarea>
2569
+ </div>
2570
+
2571
+ <!-- Task list -->
2572
+ <div>
2573
+ <div style="font-size:11px;font-weight:600;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px">
2574
+ Tasks — <span x-text="todayData.tasks.filter(t=>!t.done).length"></span> pending
2575
+ </div>
2576
+
2577
+ <!-- Carried over -->
2578
+ <template x-if="todayData.tasks.some(t => t.carriedOver && !t.done)">
2579
+ <div style="margin-bottom:8px">
2580
+ <div style="font-size:10px;color:var(--ink-3);margin-bottom:4px">↩ Carried over</div>
2581
+ <template x-for="task in todayData.tasks.filter(t => t.carriedOver && !t.done)" :key="task.id">
2582
+ <div style="display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--rule)">
2583
+ <input type="checkbox" :checked="task.done" @change="toggleTodayTask(task.id)" style="flex-shrink:0;cursor:pointer" />
2584
+ <span x-text="task.text" style="flex:1;font-size:13px;color:var(--ink-2)"></span>
2585
+ <button @click="deleteTodayTask(task.id)" style="background:none;border:none;color:var(--ink-3);cursor:pointer;font-size:14px;padding:0 2px;line-height:1" title="Remove">×</button>
2586
+ </div>
2587
+ </template>
2588
+ </div>
2589
+ </template>
2590
+
2591
+ <!-- Today's tasks -->
2592
+ <template x-for="task in todayData.tasks.filter(t => !t.carriedOver && !t.done)" :key="task.id">
2593
+ <div style="display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--rule)">
2594
+ <input type="checkbox" :checked="task.done" @change="toggleTodayTask(task.id)" style="flex-shrink:0;cursor:pointer" />
2595
+ <span x-text="task.text" style="flex:1;font-size:13px;color:var(--ink)"></span>
2596
+ <button @click="deleteTodayTask(task.id)" style="background:none;border:none;color:var(--ink-3);cursor:pointer;font-size:14px;padding:0 2px;line-height:1" title="Remove">×</button>
2597
+ </div>
2598
+ </template>
2599
+
2600
+ <!-- Done tasks -->
2601
+ <template x-if="todayData.tasks.some(t => t.done)">
2602
+ <div style="margin-top:12px">
2603
+ <div style="font-size:10px;color:var(--ink-3);margin-bottom:4px">✓ Done</div>
2604
+ <template x-for="task in todayData.tasks.filter(t => t.done)" :key="task.id">
2605
+ <div style="display:flex;align-items:center;gap:8px;padding:6px 0;border-bottom:1px solid var(--rule);opacity:0.5">
2606
+ <input type="checkbox" :checked="task.done" @change="toggleTodayTask(task.id)" style="flex-shrink:0;cursor:pointer" />
2607
+ <span x-text="task.text" style="flex:1;font-size:13px;text-decoration:line-through;color:var(--ink-3)"></span>
2608
+ <button @click="deleteTodayTask(task.id)" style="background:none;border:none;color:var(--ink-3);cursor:pointer;font-size:14px;padding:0 2px;line-height:1" title="Remove">×</button>
2609
+ </div>
2610
+ </template>
2611
+ </div>
2612
+ </template>
2613
+
2614
+ <!-- Add task -->
2615
+ <div style="display:flex;gap:8px;margin-top:12px">
2616
+ <input class="search-input" type="text" x-model="todayNewTask" placeholder="Add a task…" style="flex:1;font-size:13px" @keydown.enter="addTodayTask()" />
2617
+ <button class="btn btn-primary btn-sm" @click="addTodayTask()" :disabled="!todayNewTask.trim()">Add</button>
2618
+ </div>
2619
+ </div>
2620
+
2621
+ </div>
2622
+ </template>
2623
+ </div>
2624
+ </div>
2625
+ </template>
2626
+
2537
2627
  <!-- Plans view -->
2538
2628
  <template x-if="view === 'plans' && !selectedSession">
2539
2629
  <div style="display:flex;flex-direction:column;height:100%;overflow:hidden;">
@@ -4338,6 +4428,11 @@
4338
4428
  planExportMsg: '',
4339
4429
  planExportOpen: false,
4340
4430
  planShareMsg: '',
4431
+ todayData: null,
4432
+ todayLoading: false,
4433
+ todayNewTask: '',
4434
+ todaySaveTimer: null,
4435
+ todayCopied: false,
4341
4436
  personalNotes: [],
4342
4437
  personalNotesLoading: false,
4343
4438
  selectedNote: null,
@@ -4518,6 +4613,7 @@
4518
4613
  this.loadStatus();
4519
4614
  this.loadInsights();
4520
4615
  // Load counts for nav badges without blocking
4616
+ this.loadToday();
4521
4617
  this.loadPlans();
4522
4618
  this.loadAgents();
4523
4619
  this.loadTools();
@@ -4529,7 +4625,8 @@
4529
4625
  const mNote = hash.match(/^#\/note\/(.+)$/);
4530
4626
  if (mSession) { this.view = 'sessions'; await this.openSessionById(mSession[2], mSession[1]); }
4531
4627
  if (mNote) {
4532
- await this.loadPersonalNotes();
4628
+ const notes = await fetch('/api/notes').then(r => r.json()).catch(() => []);
4629
+ this.personalNotes = notes;
4533
4630
  const note = this.personalNotes.find(n => n.filename === mNote[1]);
4534
4631
  if (note) { this.view = 'notes'; this.selectedNote = note; }
4535
4632
  }
@@ -4539,6 +4636,7 @@
4539
4636
  },
4540
4637
 
4541
4638
  initView(v) {
4639
+ if (v === 'today') { this.loadToday(); }
4542
4640
  if (v === 'dashboard') { this.loadStats(); this.loadInsights(); }
4543
4641
  if (v === 'projects') { this.loadProjects(); }
4544
4642
  if (v === 'memory') { this.loadMemory(); }
@@ -5492,6 +5590,68 @@
5492
5590
  this.selectedSession = null;
5493
5591
  },
5494
5592
 
5593
+ async loadToday() {
5594
+ this.todayLoading = true;
5595
+ this.todayData = await fetch('/api/today').then(r => r.json()).catch(() => null);
5596
+ this.todayLoading = false;
5597
+ },
5598
+
5599
+ saveToday() {
5600
+ clearTimeout(this.todaySaveTimer);
5601
+ this.todaySaveTimer = setTimeout(async () => {
5602
+ if (!this.todayData) return;
5603
+ await fetch('/api/today', {
5604
+ method: 'PUT',
5605
+ headers: { 'Content-Type': 'application/json' },
5606
+ body: JSON.stringify({ context: this.todayData.context, tasks: this.todayData.tasks }),
5607
+ });
5608
+ }, 400);
5609
+ },
5610
+
5611
+ addTodayTask() {
5612
+ const text = this.todayNewTask.trim();
5613
+ if (!text) return;
5614
+ if (!this.todayData) return;
5615
+ this.todayData.tasks.push({
5616
+ id: Math.random().toString(36).slice(2),
5617
+ text,
5618
+ done: false,
5619
+ carriedOver: false,
5620
+ createdAt: new Date().toISOString(),
5621
+ });
5622
+ this.todayNewTask = '';
5623
+ this.saveToday();
5624
+ },
5625
+
5626
+ toggleTodayTask(id) {
5627
+ const task = this.todayData?.tasks.find(t => t.id === id);
5628
+ if (task) { task.done = !task.done; this.saveToday(); }
5629
+ },
5630
+
5631
+ deleteTodayTask(id) {
5632
+ if (!this.todayData) return;
5633
+ this.todayData.tasks = this.todayData.tasks.filter(t => t.id !== id);
5634
+ this.saveToday();
5635
+ },
5636
+
5637
+ copyTodayForClaude() {
5638
+ if (!this.todayData) return;
5639
+ const date = this.todayData.date;
5640
+ const ctx = this.todayData.context ? `\nContext: ${this.todayData.context}` : '';
5641
+ const carried = this.todayData.tasks.filter(t => t.carriedOver && !t.done);
5642
+ const fresh = this.todayData.tasks.filter(t => !t.carriedOver && !t.done);
5643
+ const done = this.todayData.tasks.filter(t => t.done);
5644
+ let text = `## My day — ${date}${ctx}\n`;
5645
+ if (carried.length) text += `\n### Carried over from yesterday:\n${carried.map(t => `- [ ] ${t.text}`).join('\n')}\n`;
5646
+ if (fresh.length) text += `\n### Today's tasks:\n${fresh.map(t => `- [ ] ${t.text}`).join('\n')}\n`;
5647
+ if (done.length) text += `\n### Already done:\n${done.map(t => `- [x] ${t.text}`).join('\n')}\n`;
5648
+ text += `\nPlease prioritize my pending tasks given the context and suggest a realistic order for today.`;
5649
+ navigator.clipboard.writeText(text).then(() => {
5650
+ this.todayCopied = true;
5651
+ setTimeout(() => this.todayCopied = false, 2000);
5652
+ });
5653
+ },
5654
+
5495
5655
  async saveSettings() {
5496
5656
  this.settingsSaving = true; this.settingsMsg = '';
5497
5657
  try {
package/server.js CHANGED
@@ -1906,10 +1906,35 @@ When the user asks you to "save a note", "add to notes", "guarda esto como nota"
1906
1906
 
1907
1907
  <the content the user wants to save>
1908
1908
  \`\`\`
1909
- 2. Use the Bash tool to write the file.
1909
+ 2. Use the Write tool to create the file (not Bash).
1910
1910
  3. Confirm with: "Saved to Notes: http://localhost:3141/#/note/<filename>"
1911
1911
 
1912
- The notes directory may not exist yet — create it if needed with \`mkdir -p\`.
1912
+ The notes directory may not exist yet — the app creates it automatically on first load.
1913
+
1914
+ ## Daily TODOs (Today view)
1915
+
1916
+ When the user asks to add a task "for today", "for tomorrow", "to review later", or similar:
1917
+ 1. Determine the target date (today = current date, tomorrow = current date + 1 day)
1918
+ 2. Read the existing file if it exists: \`~/.claude/claude-home/todos/YYYY-MM-DD.json\`
1919
+ 3. Use the Write tool to save the updated file with this format:
1920
+ \`\`\`json
1921
+ {
1922
+ "date": "YYYY-MM-DD",
1923
+ "context": "",
1924
+ "tasks": [
1925
+ {
1926
+ "id": "<random 8 char alphanumeric>",
1927
+ "text": "<task description>",
1928
+ "done": false,
1929
+ "carriedOver": false,
1930
+ "createdAt": "<current ISO date>"
1931
+ }
1932
+ ]
1933
+ }
1934
+ \`\`\`
1935
+ 4. Confirm with: "Added to Today: http://localhost:3141 (Today section)"
1936
+
1937
+ The todos directory may not exist yet — the app creates it automatically on first load.
1913
1938
  `;
1914
1939
 
1915
1940
  app.get('/api/notes/claude-md-status', (req, res) => {
@@ -1922,10 +1947,30 @@ app.get('/api/notes/claude-md-status', (req, res) => {
1922
1947
 
1923
1948
  app.post('/api/notes/setup-claude', (req, res) => {
1924
1949
  const claudeMdPath = path.join(os.homedir(), '.claude', 'CLAUDE.md');
1950
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
1925
1951
  try {
1926
1952
  const current = fs.existsSync(claudeMdPath) ? fs.readFileSync(claudeMdPath, 'utf8') : '';
1927
1953
  if (current.includes('Personal Notes')) return res.json({ ok: true, alreadyInstalled: true });
1954
+ // Append CLAUDE.md snippet
1928
1955
  fs.writeFileSync(claudeMdPath, current + '\n' + NOTES_CLAUDE_MD_SNIPPET, 'utf8');
1956
+ // Add Write permission for notes dir to settings.json
1957
+ try {
1958
+ const settings = fs.existsSync(settingsPath) ? JSON.parse(fs.readFileSync(settingsPath, 'utf8')) : {};
1959
+ if (!settings.permissions) settings.permissions = {};
1960
+ if (!settings.permissions.allow) settings.permissions.allow = [];
1961
+ const rules = [
1962
+ `Write(${path.join(os.homedir(), '.claude', 'claude-home', 'notes', '*')})`,
1963
+ `Write(${path.join(os.homedir(), '.claude', 'claude-home', 'todos', '*')})`,
1964
+ ];
1965
+ let changed = false;
1966
+ for (const rule of rules) {
1967
+ if (!settings.permissions.allow.includes(rule)) {
1968
+ settings.permissions.allow.push(rule);
1969
+ changed = true;
1970
+ }
1971
+ }
1972
+ if (changed) fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
1973
+ } catch {}
1929
1974
  res.json({ ok: true });
1930
1975
  } catch (e) { res.status(500).json({ error: e.message }); }
1931
1976
  });
@@ -1982,6 +2027,56 @@ app.delete('/api/notes/:filename', (req, res) => {
1982
2027
  } catch (e) { res.status(500).json({ error: e.message }); }
1983
2028
  });
1984
2029
 
2030
+ // ─── Today / Daily TODOs ──────────────────────────────────────────────────────
2031
+ const TODOS_DIR = path.join(DATA_DIR, 'todos');
2032
+ function ensureTodosDir() { if (!fs.existsSync(TODOS_DIR)) fs.mkdirSync(TODOS_DIR, { recursive: true }); }
2033
+
2034
+ function todayStr() { return new Date().toISOString().slice(0, 10); }
2035
+
2036
+ function readTodosFile(dateStr) {
2037
+ const filePath = path.join(TODOS_DIR, `${dateStr}.json`);
2038
+ try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { return null; }
2039
+ }
2040
+
2041
+ function writeTodosFile(dateStr, data) {
2042
+ ensureTodosDir();
2043
+ fs.writeFileSync(path.join(TODOS_DIR, `${dateStr}.json`), JSON.stringify(data, null, 2), 'utf8');
2044
+ }
2045
+
2046
+ app.get('/api/today', (req, res) => {
2047
+ ensureTodosDir();
2048
+ const today = todayStr();
2049
+ let data = readTodosFile(today);
2050
+ if (!data) {
2051
+ // Carry over undone tasks from most recent previous day
2052
+ const files = fs.readdirSync(TODOS_DIR)
2053
+ .filter(f => f.endsWith('.json') && f.slice(0, 10) < today)
2054
+ .sort().reverse();
2055
+ const carriedTasks = [];
2056
+ if (files.length > 0) {
2057
+ const prev = readTodosFile(files[0].slice(0, 10));
2058
+ if (prev?.tasks) {
2059
+ prev.tasks.filter(t => !t.done).forEach(t => {
2060
+ carriedTasks.push({ ...t, carriedOver: true, done: false });
2061
+ });
2062
+ }
2063
+ }
2064
+ data = { date: today, context: '', tasks: carriedTasks };
2065
+ writeTodosFile(today, data);
2066
+ }
2067
+ res.json(data);
2068
+ });
2069
+
2070
+ app.put('/api/today', (req, res) => {
2071
+ const today = todayStr();
2072
+ const { context, tasks } = req.body;
2073
+ const current = readTodosFile(today) || { date: today, context: '', tasks: [] };
2074
+ if (context !== undefined) current.context = context;
2075
+ if (tasks !== undefined) current.tasks = tasks;
2076
+ writeTodosFile(today, current);
2077
+ res.json(current);
2078
+ });
2079
+
1985
2080
  // ─── Start ────────────────────────────────────────────────────────────────────
1986
2081
 
1987
2082
  function startServer(port) {