persnally 2.5.0 → 2.5.2
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 +4 -2
- package/build/src/cli.js +1 -1
- package/build/src/dashboard.html +10 -7
- package/build/src/store.d.ts +2 -0
- package/build/src/store.js +9 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,14 +93,16 @@ persnallyd import claude|claude-code|chatgpt|git <path>
|
|
|
93
93
|
persnallyd scope <client> <categories> # limit what a client can read
|
|
94
94
|
persnallyd profile # synthesize the profile
|
|
95
95
|
persnallyd consolidate # reflect now: refresh decay, add behavior patterns
|
|
96
|
+
persnallyd voice # refresh your "how you write" fingerprint (offline)
|
|
96
97
|
persnallyd show [topics|events|profile]
|
|
97
|
-
persnallyd
|
|
98
|
+
persnallyd activity # context-read engagement over time (retention pulse)
|
|
99
|
+
persnallyd forget <topic> | --all | --batch <id> | --style <dim> <pattern>
|
|
98
100
|
persnallyd config set-key <sk-ant-…> # key for the background daemon
|
|
99
101
|
```
|
|
100
102
|
|
|
101
103
|
## Status
|
|
102
104
|
|
|
103
|
-
Early and moving fast — see [ROADMAP.md](./ROADMAP.md). Today: import from Claude, ChatGPT, Claude Code, and git; a decay-weighted interest graph; an evidence-linked profile; a local dashboard; per-client permission scoping; nightly consolidation; and the MCP layer that serves it all. Next: cross-tool context everywhere, then a behavior model that can answer *what would I do here?*
|
|
105
|
+
Early and moving fast — see [ROADMAP.md](./ROADMAP.md). Today: import from Claude, ChatGPT, Claude Code, and git; a decay-weighted interest graph; an evidence-linked profile; a voice & convention layer so connected tools answer the way you write; a local dashboard with full provenance and one-click deletion; per-client permission scoping; nightly consolidation; and the MCP layer that serves it all. Next: cross-tool context everywhere, then a behavior model that can answer *what would I do here?*
|
|
104
106
|
|
|
105
107
|
## License
|
|
106
108
|
|
package/build/src/cli.js
CHANGED
|
@@ -471,7 +471,7 @@ async function main() {
|
|
|
471
471
|
console.log("No activity yet — run an import or connect a client.");
|
|
472
472
|
return;
|
|
473
473
|
}
|
|
474
|
-
const verdict = a.retainedWeek2 === null ? `in progress (day ${a.
|
|
474
|
+
const verdict = a.retainedWeek2 === null ? `in progress (day ${a.daysSinceFirstRead}/14 of reads)` : a.retainedWeek2 ? "active ✓" : "inactive ✗";
|
|
475
475
|
console.log(`Onboarded ${a.daysSinceFirst}d ago · ${a.totalReads} context read(s) total`);
|
|
476
476
|
console.log(`Reads: ${a.reads7d} this week · ${a.reads30d} this month`);
|
|
477
477
|
console.log(`Active: ${a.activeDays7d}/7 days · ${a.activeDays14d}/14 days`);
|
package/build/src/dashboard.html
CHANGED
|
@@ -55,13 +55,14 @@
|
|
|
55
55
|
/* ── delta strip ── */
|
|
56
56
|
.delta {
|
|
57
57
|
background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
|
|
58
|
-
padding: 14px 18px; margin-bottom: 32px; display: flex;
|
|
58
|
+
padding: 14px 18px; margin-bottom: 32px; display: flex; flex-direction: column; align-items: flex-start; gap: 9px;
|
|
59
59
|
}
|
|
60
|
-
.delta .lead { font-size: 11px; letter-spacing: 1.3px; text-transform: uppercase; color: var(--dim);
|
|
61
|
-
.delta .items {
|
|
62
|
-
.delta .di {
|
|
60
|
+
.delta .lead { font-size: 11px; letter-spacing: 1.3px; text-transform: uppercase; color: var(--dim); }
|
|
61
|
+
.delta .items { font-size: 14px; line-height: 1.7; color: var(--dim); }
|
|
62
|
+
.delta .di { color: var(--dim); }
|
|
63
63
|
.delta .di b { color: var(--text); font-weight: 600; }
|
|
64
64
|
.delta .di .arr { color: var(--text); font-weight: 700; }
|
|
65
|
+
.delta .sep { color: var(--faint); }
|
|
65
66
|
|
|
66
67
|
/* ── hero ── */
|
|
67
68
|
.hero { margin-bottom: 8px; }
|
|
@@ -188,6 +189,8 @@
|
|
|
188
189
|
.bar-track { width: 64px; }
|
|
189
190
|
td { padding: 8px 6px; font-size: 13px; }
|
|
190
191
|
.del { padding: 3px 7px; }
|
|
192
|
+
.delta .items { display: flex; flex-direction: column; gap: 6px; line-height: 1.45; }
|
|
193
|
+
.delta .sep { display: none; } /* stacked on phones — separators only read inline */
|
|
191
194
|
}
|
|
192
195
|
@media (prefers-reduced-motion: reduce) { .reveal { animation: none; opacity: 1; transform: none; } .read.fresh { animation: none; } }
|
|
193
196
|
</style>
|
|
@@ -327,7 +330,7 @@ function renderDelta(topics, reads, assertions) {
|
|
|
327
330
|
const na = assertions.filter(a => a.ts > now).length;
|
|
328
331
|
if (na) items.push(`<span class="di"><b>${na}</b> new ${na>1?"patterns":"pattern"} noticed</span>`);
|
|
329
332
|
if (!items.length) { el.className = "delta reveal"; el.innerHTML = `<span class="lead">all caught up</span><div class="items"><span class="di">steady since ${timeAgo(now)} — the engine keeps it current while you work.</span></div>`; return; }
|
|
330
|
-
el.className = "delta reveal"; el.innerHTML = `<span class="lead">since you last looked</span><div class="items">${items.join("")}</div>`;
|
|
333
|
+
el.className = "delta reveal"; el.innerHTML = `<span class="lead">since you last looked</span><div class="items">${items.join(' <span class="sep">·</span> ')}</div>`;
|
|
331
334
|
}
|
|
332
335
|
function saveSnapshot(topics) { const w = {}; topics.forEach(t => w[t.topic_key]=t.weight); localStorage.setItem(SNAP_KEY, JSON.stringify({ t:new Date().toISOString(), weights:w })); }
|
|
333
336
|
|
|
@@ -434,7 +437,7 @@ function renderEngage(a) {
|
|
|
434
437
|
const c = d.reads ? (today ? "var(--text)" : "var(--dim)") : "var(--line-2)";
|
|
435
438
|
return `<i style="height:${h}px;background:${c}" title="${esc(d.date)}: ${d.reads}"></i>`;
|
|
436
439
|
}).join("");
|
|
437
|
-
const ret = a.retainedWeek2 === null ? `day ${a.
|
|
440
|
+
const ret = a.retainedWeek2 === null ? `day ${a.daysSinceFirstRead}/14` : (a.retainedWeek2 ? "active ✓" : "inactive ✗");
|
|
438
441
|
el.innerHTML = `<span><b>${a.reads7d}</b> this week</span><span>active <b>${a.activeDays14d}</b> of last 14 days</span>` +
|
|
439
442
|
`<span class="spark" title="context reads, last 14 days">${bars}</span><span class="rk">week-2: ${esc(ret)}</span>`;
|
|
440
443
|
}
|
|
@@ -707,7 +710,7 @@ function DEMO_DATA() {
|
|
|
707
710
|
{ ts:iso(86400000*2), payload:{ claim:"You consistently choose local-first, auditable tools over cloud convenience.", kind:"preference", confidence:0.86 }, provenance:{ kind:"derived" } },
|
|
708
711
|
{ ts:iso(86400000*6), payload:{ claim:"You generate new ideas fastest right when your main bet stalls.", kind:"behavior", confidence:0.71 }, provenance:{ kind:"derived" } },
|
|
709
712
|
],
|
|
710
|
-
activity: { firstEventAt: iso(86400000*214), lastReadAt: iso(3600000*2), daysSinceFirst: 214, totalReads: 1247, reads7d: 33, reads30d: 142, activeDays7d: 5, activeDays14d: 12, retainedWeek2: true, daily: [2,0,3,5,1,4,6,2,0,3,7,5,8,4].map((r,i)=>({ date:new Date(now-(13-i)*86400000).toISOString().slice(0,10), reads:r })) },
|
|
713
|
+
activity: { firstEventAt: iso(86400000*214), firstReadAt: iso(86400000*210), lastReadAt: iso(3600000*2), daysSinceFirst: 214, daysSinceFirstRead: 210, totalReads: 1247, reads7d: 33, reads30d: 142, activeDays7d: 5, activeDays14d: 12, retainedWeek2: true, daily: [2,0,3,5,1,4,6,2,0,3,7,5,8,4].map((r,i)=>({ date:new Date(now-(13-i)*86400000).toISOString().slice(0,10), reads:r })) },
|
|
711
714
|
voice: {
|
|
712
715
|
pack: "Write like this user: terse — short, declarative sentences; leads with imperatives, minimal preamble; states things flatly; no emoji; casual register (lowercases “i”); recurring phrasing: “be 100% sure”, “industry best practices”, “validate whether it's valid or not”.",
|
|
713
716
|
items: [
|
package/build/src/store.d.ts
CHANGED
|
@@ -37,8 +37,10 @@ export interface StoredProfile {
|
|
|
37
37
|
}
|
|
38
38
|
export interface Activity {
|
|
39
39
|
firstEventAt: string | null;
|
|
40
|
+
firstReadAt: string | null;
|
|
40
41
|
lastReadAt: string | null;
|
|
41
42
|
daysSinceFirst: number;
|
|
43
|
+
daysSinceFirstRead: number;
|
|
42
44
|
totalReads: number;
|
|
43
45
|
reads7d: number;
|
|
44
46
|
reads30d: number;
|
package/build/src/store.js
CHANGED
|
@@ -161,6 +161,11 @@ export class EventStore {
|
|
|
161
161
|
const reads = this.query({ type: "context.read", limit: 1_000_000 }); // ts DESC
|
|
162
162
|
const firstEventAt = this.db.prepare("SELECT MIN(ts) m FROM events").get().m;
|
|
163
163
|
const firstMs = firstEventAt ? new Date(firstEventAt).getTime() : null;
|
|
164
|
+
// Retention is anchored to when serving actually began (the first read), not
|
|
165
|
+
// onboarding — so a gap between setup and the first read can't read as a
|
|
166
|
+
// false "not retained". For a fresh install the two are minutes apart.
|
|
167
|
+
const firstReadAt = reads.length ? reads[reads.length - 1].ts : null;
|
|
168
|
+
const firstReadMs = firstReadAt ? new Date(firstReadAt).getTime() : null;
|
|
164
169
|
const daily = new Map();
|
|
165
170
|
for (let i = 13; i >= 0; i--)
|
|
166
171
|
daily.set(dayKey(now - i * DAY), 0);
|
|
@@ -181,19 +186,21 @@ export class EventStore {
|
|
|
181
186
|
reads30d++;
|
|
182
187
|
if (daily.has(k))
|
|
183
188
|
daily.set(k, (daily.get(k) ?? 0) + 1);
|
|
184
|
-
if (
|
|
189
|
+
if (firstReadMs !== null && t >= firstReadMs + 7 * DAY && t < firstReadMs + 14 * DAY)
|
|
185
190
|
week2Read = true;
|
|
186
191
|
}
|
|
187
192
|
return {
|
|
188
193
|
firstEventAt,
|
|
194
|
+
firstReadAt,
|
|
189
195
|
lastReadAt: reads.length ? reads[0].ts : null,
|
|
190
196
|
daysSinceFirst: firstMs !== null ? Math.max(0, Math.floor((now - firstMs) / DAY)) : 0,
|
|
197
|
+
daysSinceFirstRead: firstReadMs !== null ? Math.max(0, Math.floor((now - firstReadMs) / DAY)) : 0,
|
|
191
198
|
totalReads: reads.length,
|
|
192
199
|
reads7d,
|
|
193
200
|
reads30d,
|
|
194
201
|
activeDays7d: days7.size,
|
|
195
202
|
activeDays14d: days14.size,
|
|
196
|
-
retainedWeek2:
|
|
203
|
+
retainedWeek2: firstReadMs !== null && now >= firstReadMs + 14 * DAY ? week2Read : null,
|
|
197
204
|
daily: [...daily.entries()].map(([date, r]) => ({ date, reads: r })),
|
|
198
205
|
};
|
|
199
206
|
}
|