claude-home 1.6.0 → 1.6.1
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 +2 -0
- package/package.json +1 -1
- package/public/index.html +82 -21
- package/server.js +77 -0
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@ Claude Code is powerful but headless. Everything lives in files scattered across
|
|
|
17
17
|
### Sessions
|
|
18
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
19
|
|
|
20
|
+
**Session snapshot** — save a structured summary of any conversation as a note. Opens the **Export** menu and click *Snapshot → Note*. The note includes project/branch/date metadata, a numbered list of all your prompts, and an empty *Notes* section for your annotations. Tagged `snapshot` automatically with a backlink to the original session.
|
|
21
|
+
|
|
20
22
|
### Today
|
|
21
23
|
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
24
|
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -496,6 +496,8 @@
|
|
|
496
496
|
}
|
|
497
497
|
.note-folder-row:hover { background: var(--canvas); }
|
|
498
498
|
.note-folder-row:hover .note-folder-rename-btn { opacity: 1 !important; }
|
|
499
|
+
.note-folder-row:hover .note-folder-delete-btn { opacity: 1 !important; }
|
|
500
|
+
.note-folder-delete-btn:hover { color: var(--red) !important; }
|
|
499
501
|
.note-folder-icon { font-size: 8px; color: var(--ink-3); }
|
|
500
502
|
.note-folder-name { flex: 1; font-weight: 500; }
|
|
501
503
|
.note-folder-count { font-size: 10px; color: var(--ink-3); background: var(--canvas); border: 1px solid var(--rule); border-radius: 10px; padding: 0 5px; }
|
|
@@ -2450,7 +2452,7 @@
|
|
|
2450
2452
|
<button class="export-drop-item" @click="copySessionMd(); exportDropOpen=false">Copy as Markdown</button>
|
|
2451
2453
|
<button class="export-drop-item" @click="shareSession(); exportDropOpen=false" :disabled="!!shareMsg"><span x-text="shareMsg || 'Publish to Gist'"></span></button>
|
|
2452
2454
|
<button class="export-drop-item" @click="navigator.clipboard.writeText(location.origin+location.pathname+'#/session/'+selectedSession.projectDir+'/'+selectedSession.sessionId);exportDropOpen=false">Copy local link</button>
|
|
2453
|
-
<button class="export-drop-item" @click="
|
|
2455
|
+
<button class="export-drop-item" @click="snapshotSession();exportDropOpen=false" :disabled="snapshottingSession" x-text="snapshottingSession ? 'Saving…' : 'Snapshot → Note'"></button>
|
|
2454
2456
|
<div style="height:1px;background:var(--rule);margin:4px 0"></div>
|
|
2455
2457
|
<button class="export-drop-item" style="color:var(--red)" @click="deleteSession();exportDropOpen=false" x-show="!deletingSession">Delete session</button>
|
|
2456
2458
|
</div>
|
|
@@ -2966,6 +2968,10 @@
|
|
|
2966
2968
|
<span style="font-size:10px;color:var(--ink-3);padding:0 2px;opacity:0;transition:opacity .15s" class="note-folder-rename-btn"
|
|
2967
2969
|
@click.stop="noteRenamingFolder=f;noteRenameFolderDraft=f" title="Rename">✎</span>
|
|
2968
2970
|
</template>
|
|
2971
|
+
<template x-if="noteRenamingFolder !== f">
|
|
2972
|
+
<span style="font-size:10px;color:var(--ink-3);padding:0 1px;opacity:0;transition:opacity .15s" class="note-folder-delete-btn"
|
|
2973
|
+
@click.stop="deletingFolder=f" title="Delete folder">✕</span>
|
|
2974
|
+
</template>
|
|
2969
2975
|
<template x-if="noteRenamingFolder === f">
|
|
2970
2976
|
<button class="btn btn-sm btn-primary" style="padding:1px 7px;font-size:11px" @click.stop="renameFolderConfirm(f)">OK</button>
|
|
2971
2977
|
</template>
|
|
@@ -3068,14 +3074,24 @@
|
|
|
3068
3074
|
<div style="padding:24px;max-width:380px">
|
|
3069
3075
|
<div style="font-size:13px;font-weight:600;margin-bottom:6px;color:var(--ink)">Your personal notepad</div>
|
|
3070
3076
|
<div style="font-size:12px;color:var(--ink-3);margin-bottom:16px;line-height:1.6">Notes are <strong>for you</strong>, not for Claude. He won't read them as context — they're yours to capture, review and revisit.</div>
|
|
3071
|
-
<div style="font-size:11px;font-weight:600;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:
|
|
3072
|
-
<div style="display:flex;flex-direction:column;gap:
|
|
3077
|
+
<div style="font-size:11px;font-weight:600;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Capture</div>
|
|
3078
|
+
<div style="display:flex;flex-direction:column;gap:5px;margin-bottom:14px">
|
|
3073
3079
|
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Save this as a note"</code>
|
|
3074
|
-
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Save this
|
|
3080
|
+
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Save this as a TIL in my notes"</code>
|
|
3075
3081
|
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Add this decision to my notes"</code>
|
|
3076
|
-
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Save this
|
|
3082
|
+
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Save this in my <em>project</em> notes folder"</code>
|
|
3083
|
+
</div>
|
|
3084
|
+
<div style="font-size:11px;font-weight:600;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Organise</div>
|
|
3085
|
+
<div style="display:flex;flex-direction:column;gap:5px;margin-bottom:14px">
|
|
3086
|
+
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Tag my last note as <em>bug</em> and <em>auth</em>"</code>
|
|
3087
|
+
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Move the note about X to the <em>project</em> folder"</code>
|
|
3088
|
+
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Pin my runbook note"</code>
|
|
3089
|
+
</div>
|
|
3090
|
+
<div style="font-size:11px;font-weight:600;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">Retrieve & act</div>
|
|
3091
|
+
<div style="display:flex;flex-direction:column;gap:5px">
|
|
3077
3092
|
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Show me my last note"</code>
|
|
3078
|
-
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"What notes do I have
|
|
3093
|
+
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"What notes do I have tagged <em>bug</em>?"</code>
|
|
3094
|
+
<code style="font-size:11px;background:var(--canvas-2);border:1px solid var(--rule);padding:3px 7px;border-radius:4px;display:block">"Add the first step of my deploy note to today's tasks"</code>
|
|
3079
3095
|
</div>
|
|
3080
3096
|
</div>
|
|
3081
3097
|
</template>
|
|
@@ -4684,6 +4700,24 @@
|
|
|
4684
4700
|
|
|
4685
4701
|
</div>
|
|
4686
4702
|
|
|
4703
|
+
<!-- Delete folder modal -->
|
|
4704
|
+
<template x-if="deletingFolder !== null">
|
|
4705
|
+
<div class="memory-new-modal" @click.self="deletingFolder=null">
|
|
4706
|
+
<div class="memory-new-form" style="width:360px">
|
|
4707
|
+
<h3>Borrar carpeta "<span x-text="deletingFolder"></span>"</h3>
|
|
4708
|
+
<p style="font-size:12px;color:var(--ink-2);line-height:1.5;margin-bottom:20px">
|
|
4709
|
+
Contiene <strong x-text="personalNotes.filter(n=>(n.folder??'')===deletingFolder).length"></strong> nota(s).
|
|
4710
|
+
¿Borras también las notas o las dejas sueltas (sin carpeta)?
|
|
4711
|
+
</p>
|
|
4712
|
+
<div style="display:flex;gap:8px;justify-content:flex-end">
|
|
4713
|
+
<button class="btn btn-sm btn-outline" @click="deletingFolder=null">Cancelar</button>
|
|
4714
|
+
<button class="btn btn-sm" style="background:var(--canvas-2,#f5f5f5);border:1px solid var(--rule);color:var(--ink)" @click="deleteFolderConfirm('orphan')">Soltar notas</button>
|
|
4715
|
+
<button class="btn btn-sm" style="background:var(--red-dim,#3a1a1a);color:var(--red);border:1px solid var(--red)" @click="deleteFolderConfirm('delete')">Borrar todo</button>
|
|
4716
|
+
</div>
|
|
4717
|
+
</div>
|
|
4718
|
+
</div>
|
|
4719
|
+
</template>
|
|
4720
|
+
|
|
4687
4721
|
<!-- New Memory modal -->
|
|
4688
4722
|
<template x-if="memoryNewModal">
|
|
4689
4723
|
<div class="memory-new-modal" @click.self="memoryNewModal=false">
|
|
@@ -4779,6 +4813,7 @@
|
|
|
4779
4813
|
selectedPlan: null,
|
|
4780
4814
|
plansSearch: '',
|
|
4781
4815
|
deletingSession: false,
|
|
4816
|
+
snapshottingSession: false,
|
|
4782
4817
|
exportDropOpen: false,
|
|
4783
4818
|
exportMsg: '',
|
|
4784
4819
|
cleanMode: true,
|
|
@@ -4819,6 +4854,7 @@
|
|
|
4819
4854
|
noteFolders: [],
|
|
4820
4855
|
noteRenamingFolder: null,
|
|
4821
4856
|
noteRenameFolderDraft: '',
|
|
4857
|
+
deletingFolder: null,
|
|
4822
4858
|
noteTagMenu: null,
|
|
4823
4859
|
noteTagRenaming: false,
|
|
4824
4860
|
noteTagRenameDraft: '',
|
|
@@ -5489,6 +5525,29 @@
|
|
|
5489
5525
|
this.noteRenameFolderDraft = '';
|
|
5490
5526
|
},
|
|
5491
5527
|
|
|
5528
|
+
async deleteFolderConfirm(action) {
|
|
5529
|
+
const folder = this.deletingFolder;
|
|
5530
|
+
if (!folder) return;
|
|
5531
|
+
const res = await fetch(`/api/notes/folders/${encodeURIComponent(folder)}?action=${action}`, {
|
|
5532
|
+
method: 'DELETE',
|
|
5533
|
+
}).then(r => r.json()).catch(() => null);
|
|
5534
|
+
if (!res?.ok) { alert(res?.error || 'Error al borrar la carpeta'); return; }
|
|
5535
|
+
this.noteFolders = this.noteFolders.filter(f => f !== folder);
|
|
5536
|
+
if (action === 'orphan') {
|
|
5537
|
+
for (const n of this.personalNotes) {
|
|
5538
|
+
if ((n.folder ?? '') === folder) { n.folder = ''; n.path = n.filename; }
|
|
5539
|
+
}
|
|
5540
|
+
if (this.selectedNote?.folder === folder) {
|
|
5541
|
+
this.selectedNote.folder = ''; this.selectedNote.path = this.selectedNote.filename;
|
|
5542
|
+
}
|
|
5543
|
+
} else {
|
|
5544
|
+
this.personalNotes = this.personalNotes.filter(n => (n.folder ?? '') !== folder);
|
|
5545
|
+
if (this.selectedNote?.folder === folder) { this.selectedNote = null; this.noteEditing = false; }
|
|
5546
|
+
}
|
|
5547
|
+
if (this.noteFolderPath === folder) { this.noteFolderPath = ''; this.selectedNote = null; }
|
|
5548
|
+
this.deletingFolder = null;
|
|
5549
|
+
},
|
|
5550
|
+
|
|
5492
5551
|
noteAllTags() {
|
|
5493
5552
|
const counts = {};
|
|
5494
5553
|
const scoped = this.personalNotes.filter(n => (n.folder ?? '') === this.noteFolderPath);
|
|
@@ -6234,21 +6293,23 @@
|
|
|
6234
6293
|
window.location.hash = `#/note/${updated.path}`;
|
|
6235
6294
|
},
|
|
6236
6295
|
|
|
6237
|
-
async
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6296
|
+
async snapshotSession() {
|
|
6297
|
+
if (!this.selectedSession || this.snapshottingSession) return;
|
|
6298
|
+
this.snapshottingSession = true;
|
|
6299
|
+
try {
|
|
6300
|
+
const { projectDir, sessionId } = this.selectedSession;
|
|
6301
|
+
const note = await fetch(`/api/sessions/${projectDir}/${sessionId}/snapshot`, {
|
|
6302
|
+
method: 'POST',
|
|
6303
|
+
}).then(r => r.json());
|
|
6304
|
+
if (note.error) { alert(note.error); return; }
|
|
6305
|
+
if (!this.personalNotes.find(n => n.path === note.path)) this.personalNotes.unshift(note);
|
|
6306
|
+
this.selectedNote = note;
|
|
6307
|
+
this.noteEditing = false;
|
|
6308
|
+
this.view = 'notes';
|
|
6309
|
+
this.selectedSession = null;
|
|
6310
|
+
} finally {
|
|
6311
|
+
this.snapshottingSession = false;
|
|
6312
|
+
}
|
|
6252
6313
|
},
|
|
6253
6314
|
|
|
6254
6315
|
async loadToday() {
|
package/server.js
CHANGED
|
@@ -1799,6 +1799,58 @@ app.get('/api/sessions/:project/:sessionId/export', async (req, res) => {
|
|
|
1799
1799
|
} catch (e) { res.status(500).json({ error: e.message }); }
|
|
1800
1800
|
});
|
|
1801
1801
|
|
|
1802
|
+
// POST /api/sessions/:project/:sessionId/snapshot — create a note with session summary
|
|
1803
|
+
app.post('/api/sessions/:project/:sessionId/snapshot', async (req, res) => {
|
|
1804
|
+
const { project, sessionId } = req.params;
|
|
1805
|
+
const filePath = path.join(PROJECTS_DIR, project, `${sessionId}.jsonl`);
|
|
1806
|
+
if (!filePath.startsWith(PROJECTS_DIR)) return res.status(400).json({ error: 'invalid path' });
|
|
1807
|
+
try {
|
|
1808
|
+
const messages = await parseJsonl(filePath);
|
|
1809
|
+
if (!messages.length) return res.status(404).json({ error: 'session not found or empty' });
|
|
1810
|
+
|
|
1811
|
+
// Extract metadata
|
|
1812
|
+
const first = messages[0];
|
|
1813
|
+
const branch = first.gitBranch || '';
|
|
1814
|
+
const date = first.timestamp ? first.timestamp.slice(0, 10) : new Date().toISOString().slice(0, 10);
|
|
1815
|
+
|
|
1816
|
+
// Extract user prompts (skip <command-name> tool messages)
|
|
1817
|
+
const prompts = [];
|
|
1818
|
+
for (const m of messages) {
|
|
1819
|
+
if (m.type !== 'user') continue;
|
|
1820
|
+
const c = m.message?.content;
|
|
1821
|
+
let text = '';
|
|
1822
|
+
if (typeof c === 'string') text = c;
|
|
1823
|
+
else if (Array.isArray(c)) {
|
|
1824
|
+
text = c.filter(b => b.type === 'text' && !b.text?.includes('<command-name>')).map(b => b.text).join('\n');
|
|
1825
|
+
}
|
|
1826
|
+
text = text.trim();
|
|
1827
|
+
if (text && text.length > 2) prompts.push(text);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
const firstPrompt = prompts[0] || sessionId;
|
|
1831
|
+
const title = `Snapshot: ${firstPrompt.slice(0, 60)}${firstPrompt.length > 60 ? '…' : ''}`;
|
|
1832
|
+
|
|
1833
|
+
const projectLabel = project.replace(/^-Users-[^-]+-/, '').replace(/-/g, '/');
|
|
1834
|
+
const meta = [`**Project:** ${projectLabel}`, branch ? `**Branch:** ${branch}` : '', `**Date:** ${date}`, `**Session:** ${sessionId.slice(0, 8)}…`].filter(Boolean).join(' · ');
|
|
1835
|
+
|
|
1836
|
+
const promptList = prompts.map((p, i) => `${i + 1}. ${p.replace(/\n+/g, ' ').slice(0, 200)}${p.length > 200 ? '…' : ''}`).join('\n');
|
|
1837
|
+
|
|
1838
|
+
const content = `${meta}\n\n## Prompts\n\n${promptList}\n\n## Notes\n\n`;
|
|
1839
|
+
|
|
1840
|
+
ensureNotesDir();
|
|
1841
|
+
const slug = firstPrompt.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 40) || 'snapshot';
|
|
1842
|
+
const datePrefix = date;
|
|
1843
|
+
let filename = `${datePrefix}-snapshot-${slug}.md`;
|
|
1844
|
+
let i = 1;
|
|
1845
|
+
while (fs.existsSync(path.join(NOTES_DIR, filename))) { filename = `${datePrefix}-snapshot-${slug}-${i++}.md`; }
|
|
1846
|
+
const tagsLine = `\ntags: [snapshot]`;
|
|
1847
|
+
const sessionLine = `\nsession: ${sessionId}`;
|
|
1848
|
+
const raw = `---\ntitle: ${title}\ndate: ${new Date().toISOString()}${sessionLine}${tagsLine}\n---\n\n${content}`;
|
|
1849
|
+
fs.writeFileSync(path.join(NOTES_DIR, filename), raw, 'utf8');
|
|
1850
|
+
res.json(parseNoteFile(filename));
|
|
1851
|
+
} catch (e) { res.status(500).json({ error: e.message }); }
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1802
1854
|
// ─── App settings (claude-home specific, not Claude's settings.json) ─────────
|
|
1803
1855
|
const APP_SETTINGS_FILE = path.join(DATA_DIR, 'app-settings.json');
|
|
1804
1856
|
|
|
@@ -2078,6 +2130,31 @@ app.patch('/api/notes/folders/:name/rename', (req, res) => {
|
|
|
2078
2130
|
} catch (e) { res.status(500).json({ error: e.message }); }
|
|
2079
2131
|
});
|
|
2080
2132
|
|
|
2133
|
+
app.delete('/api/notes/folders/:name', (req, res) => {
|
|
2134
|
+
ensureNotesDir();
|
|
2135
|
+
const { name } = req.params;
|
|
2136
|
+
const { action } = req.query; // 'delete' | 'orphan'
|
|
2137
|
+
if (!name) return res.status(400).json({ error: 'name required' });
|
|
2138
|
+
if (!['delete', 'orphan'].includes(action)) return res.status(400).json({ error: 'action must be delete or orphan' });
|
|
2139
|
+
const folderPath = path.join(NOTES_DIR, name);
|
|
2140
|
+
if (!folderPath.startsWith(NOTES_DIR + path.sep) && folderPath !== NOTES_DIR) return res.status(400).json({ error: 'invalid name' });
|
|
2141
|
+
try {
|
|
2142
|
+
if (!fs.existsSync(folderPath)) return res.status(404).json({ error: 'folder not found' });
|
|
2143
|
+
const files = fs.readdirSync(folderPath).filter(f => f.endsWith('.md'));
|
|
2144
|
+
if (action === 'orphan') {
|
|
2145
|
+
for (const f of files) {
|
|
2146
|
+
let dest = path.join(NOTES_DIR, f);
|
|
2147
|
+
if (fs.existsSync(dest)) dest = path.join(NOTES_DIR, f.slice(0, -3) + '-orphaned.md');
|
|
2148
|
+
fs.renameSync(path.join(folderPath, f), dest);
|
|
2149
|
+
}
|
|
2150
|
+
} else {
|
|
2151
|
+
for (const f of files) fs.unlinkSync(path.join(folderPath, f));
|
|
2152
|
+
}
|
|
2153
|
+
try { fs.rmdirSync(folderPath); } catch {}
|
|
2154
|
+
res.json({ ok: true, action, count: files.length });
|
|
2155
|
+
} catch (e) { res.status(500).json({ error: e.message }); }
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2081
2158
|
function rewriteNoteTags(notepath, updatedTags) {
|
|
2082
2159
|
const filePath = path.join(NOTES_DIR, notepath);
|
|
2083
2160
|
const existing = parseNoteFile(notepath);
|