persnally 2.5.0 → 2.5.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 +4 -2
- package/build/src/cli.js +1 -1
- package/build/src/dashboard.html +2 -2
- 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
|
@@ -434,7 +434,7 @@ function renderEngage(a) {
|
|
|
434
434
|
const c = d.reads ? (today ? "var(--text)" : "var(--dim)") : "var(--line-2)";
|
|
435
435
|
return `<i style="height:${h}px;background:${c}" title="${esc(d.date)}: ${d.reads}"></i>`;
|
|
436
436
|
}).join("");
|
|
437
|
-
const ret = a.retainedWeek2 === null ? `day ${a.
|
|
437
|
+
const ret = a.retainedWeek2 === null ? `day ${a.daysSinceFirstRead}/14` : (a.retainedWeek2 ? "active ✓" : "inactive ✗");
|
|
438
438
|
el.innerHTML = `<span><b>${a.reads7d}</b> this week</span><span>active <b>${a.activeDays14d}</b> of last 14 days</span>` +
|
|
439
439
|
`<span class="spark" title="context reads, last 14 days">${bars}</span><span class="rk">week-2: ${esc(ret)}</span>`;
|
|
440
440
|
}
|
|
@@ -707,7 +707,7 @@ function DEMO_DATA() {
|
|
|
707
707
|
{ 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
708
|
{ 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
709
|
],
|
|
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 })) },
|
|
710
|
+
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
711
|
voice: {
|
|
712
712
|
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
713
|
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
|
}
|