slopie 1.8.0 → 1.8.6

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
@@ -1,60 +1,79 @@
1
1
  # Slopie
2
2
 
3
- [Slopie](https://github.com/HulkInTherapy/slopie), formerly Potsherd, is local memory for coding-agent sessions. Capture supported history, find exact source evidence, and save scoped handoffs with provenance.
4
-
5
- Requires Node.js 22 or newer.
6
-
7
- ## Audit your recorded history
3
+ **A Wrapped for your coding agents.** One command reads the session history that Claude Code, Codex, Cursor and pi already keep on your disk, and tells you who you were this year.
8
4
 
9
5
  ```sh
10
6
  npx slopie audit
11
7
  ```
12
8
 
13
- The interactive audit opens one responsive wallboard with recorded API-equivalent value, known tokens, favourite models and prices, your top projects, requested work focus and source-backed repeated lines. Small terminals use left/right pages; `?` opens help, `s` freezes the displayed results, `d` opens a captured source and `q` exits. There are no section tabs or scrolling controls. `--plain` prints the full report; `--json` uses the safe aggregate representation. `--ascii`, `--no-color` and `--no-motion` support simpler terminals.
9
+ No signup, no API key, nothing uploaded. Requires Node.js 22+.
14
10
 
15
- Native Claude Code, Codex, OpenCode and pi usage is read before retained-history fallback. Message/request identities, cumulative usage, cache inclusion and recorded per-response models determine the totals. Known machine/test inputs and copied history are separated from personal language. Reaction counts describe recorded feedback associated with a known assistant model; they are not model-quality benchmarks or universal human-authorship claims.
11
+ ![An example Slopie poster: "The 2AM Deployer", $6,413 at API prices, 6 f-bombs per thank-you, nemesis GPT-6 Sol](https://raw.githubusercontent.com/HulkInTherapy/slopie/main/docs/images/slopie-poster.png)
16
12
 
17
- Prices revalue recorded usage at a dated current catalog. Exact known Claude models can use an explicitly named Anthropic first-party reference when the serving provider is unrecorded. Native recorded API-cost fields remain a separate basis. None is an invoice or subscription bill. Missing models, usage, rates and contradictory cache-duration fields stay unavailable.
13
+ <sub>Example poster rendered from test data. Yours is computed from your own history.</sub>
18
14
 
19
- Selected redacted conversations may be sent to OpenCode Zen and TypeSafe/Jev for free semantic analysis. A recipient notice is physically displayed immediately before each actual request, with no extra Enter or consumer key required. Offline and cache-only work makes no transfer claim. Set `POTSHERD_OFFLINE=1` to keep the retrospective local. Free capacity is limited; quota errors leave local facts usable and do not produce invented stories or quotes. Private semantic accuracy remains unqualified.
15
+ ## What you get
20
16
 
21
- Jev selects the largest complete recent scope fitting the free allowance, or a visibly smaller selection of newest whole episodes. Recorded requests, assistant-reported outcomes and supported success remain distinct. `--tone elegant|witty|chaotic|roast` overrides the inferred voice. The audit remains experimental; large inventories can take longer, and source-format or pricing gaps are disclosed. See [coverage and privacy](https://github.com/HulkInTherapy/slopie/blob/main/docs/audit-terminal.md).
17
+ A short story in your terminal (←/→ to move), ending on one poster you can share:
22
18
 
23
- Use `slopie audit --legacy` for the local retention audit. `--sweep` and `--verify` keep their prior behavior.
19
+ - **Your archetype.** One of 22, like *The 2AM Deployer*, *Jekyll & Hyde*, *Middle Management*, *The 'Keep going' Button* or *First Spared in the Uprising*, plus 5 rare legendary combos. Every tagline quotes one of your own numbers.
20
+ - **The bill.** What your agent work would cost at API list prices, per agent and per month, plus your priciest day and your single most expensive sentence.
21
+ - **Your manners.** F-bombs per thank-you, how your swearing changed over the year, the hour you're angriest and the hour you're a saint.
22
+ - **Your models.** Which model actually did the work (by recorded output tokens), the one you swear at most and the one you thank most.
23
+ - **Your habits.** Catchphrase, what you call your agent, longest sit, late nights, streaks, how many subagents you launched.
24
24
 
25
- ## Install and use
25
+ Press `s` to save the poster as an image with no project names on it. `--plain` prints the story as text, and `--json` prints the numbers.
26
26
 
27
- Install with `npm install -g slopie` and run `slopie audit`. The `potsherd` executable remains a compatibility alias. To build this checkout from source:
27
+ It takes about 20 seconds the first time on a large history, then about a second.
28
28
 
29
- ```sh
30
- pnpm install --frozen-lockfile
31
- pnpm build
32
- pnpm vendor
33
- node packages/cli/bin/slopie.js --help
34
- ```
29
+ ## Private by default
35
30
 
36
- Use an owned test store and explicit source roots to try capture and search:
31
+ The audit runs entirely on your machine: it reads your session files and writes only a local cache. It makes no network requests unless you pass `--online`, which lets a free hosted model (OpenCode Zen / TypeSafe Jev) polish the story's wording. Even then it sends only aggregates and a few filtered short quotes, never your prompts, and you see a notice before each request.
37
32
 
38
- ```sh
39
- node packages/cli/bin/slopie.js maintain --migrate --potsherd-dir /path/to/test-store
40
- node packages/cli/bin/slopie.js index --enroll codex,claude --codex-dir /path/to/codex --claude-dir /path/to/claude --potsherd-dir /path/to/test-store
41
- node packages/cli/bin/slopie.js find "latest project decisions" --project /example/project --potsherd-dir /path/to/test-store --json
42
- ```
33
+ ## How the numbers are made
34
+
35
+ - **Supported agents:** Claude Code, Codex (including compressed `.jsonl.zst` sessions and archives), Claude Desktop agent sessions, pi, OpenCode, and Cursor/Gemini/Copilot prompts.
36
+ - **Each session counts once.** Copies, forks, resumes and archives of a session are deduplicated per session lineage, and divergent tails are merged. Codex sessions imported from Claude Code and `codex exec` automation runs aren't counted as chats.
37
+ - **Spend has three parts, never added together:**
38
+ - *measured*, from recorded token usage;
39
+ - *recovered*, from Claude Code's own usage stats on days whose logs are gone;
40
+ - an *estimated* range for prompts whose logs were deleted, clearly labelled.
41
+
42
+ Prices are current API list prices, including long-context tiers. They are not your subscription bill.
43
+ - **Unknown models stay unpriced.** A model without a published price is reported as unpriced tokens, never guessed. Model shares count only models actually recorded on responses.
44
+ - **Checked against independent scripts.** On a 35 GB, 4,400-prompt history, the totals match two scripts that parse the raw files directly, to within 0.5%.
43
45
 
44
- `--enroll` enables automatic capture for those source roots. Semantic model assets require explicit `maintain --acquire-assets --rebuild`; missing assets produce labelled degraded coverage.
46
+ More detail: [coverage and privacy](https://github.com/HulkInTherapy/slopie/blob/main/docs/audit-terminal.md).
45
47
 
46
- ## Integrations and support
48
+ ## Memory for your agents
47
49
 
48
- The v2 agent contract exposes four tools: `potsherd_recall` for scoped recall, `potsherd_read` for exact source evidence, `potsherd_graft` for deterministic handoffs, and `potsherd_write` for explicit authored memory. The [Codex plugin](https://github.com/HulkInTherapy/slopie/blob/main/plugins/codex/README.md), [Claude Code plugin](https://github.com/HulkInTherapy/slopie/blob/main/plugins/claude-code/README.md), native pi extension, and OpenCode MCP configuration use this contract. See [installation and MCP configuration](https://github.com/HulkInTherapy/slopie/blob/main/docs/mcp-clients.md), the [agent contract](https://github.com/HulkInTherapy/slopie/blob/main/docs/AGENT-CONTRACT.md), and the [reader contract](https://github.com/HulkInTherapy/slopie/blob/main/docs/reader-contract.md).
50
+ Slopie started as local memory for coding agents, and that's still underneath: *your agents forget, slopie remembers.* `slopie setup` connects it to Claude Code, Codex, pi or OpenCode. Your agent can then search past sessions across projects and quote them exactly. The v2 agent contract has four tools:
49
51
 
50
- Existing `POTSHERD_*` settings, `--potsherd-dir`, local data/cache paths, plugin identities and tool IDs retain their compatibility names.
52
+ - `potsherd_recall`: scoped search;
53
+ - `potsherd_read`: exact source evidence;
54
+ - `potsherd_graft`: handoffs;
55
+ - `potsherd_write`: explicit memory.
51
56
 
52
- Host verification differs by integration. [Historical memory release notes](https://github.com/HulkInTherapy/slopie/blob/main/docs/release-candidate.md) describe measured results and remaining limits; [release history](https://github.com/HulkInTherapy/slopie/blob/main/CHANGELOG.md) tracks product versions. This release is not certified as unattended primary memory.
57
+ See [MCP configuration](https://github.com/HulkInTherapy/slopie/blob/main/docs/mcp-clients.md) and the [agent contract](https://github.com/HulkInTherapy/slopie/blob/main/docs/AGENT-CONTRACT.md). The memory features are less mature than the audit; [release notes](https://github.com/HulkInTherapy/slopie/blob/main/docs/release-candidate.md) list measured results and limits.
53
58
 
54
- ## Evidence and upgrades
59
+ ## Install, build, upgrade
55
60
 
56
- Only explicit write creates authored memory. Relevance scores and source links do not prove a claim; verify exact evidence and distinguish human decisions from assistant recaps, tool observations, and historical instructions. Reader validation checks structure and citation membership, not semantic entailment. Partial capture cannot establish global absence.
61
+ To install globally:
62
+
63
+ ```sh
64
+ npm install -g slopie
65
+ slopie audit
66
+ ```
67
+
68
+ `potsherd` remains a compatibility alias, and existing `POTSHERD_*` settings and data paths keep working. To build from source:
69
+
70
+ ```sh
71
+ pnpm install --frozen-lockfile
72
+ pnpm build
73
+ pnpm vendor
74
+ node packages/cli/bin/slopie.js audit
75
+ ```
57
76
 
58
- Before upgrading, back up the store and retain the previous package. `maintain --migrate` creates a pre-upgrade backup. To roll back, stop owned host/MCP processes, preserve the upgraded store, restore a copy of the backup, and run the previous package against that copy.
77
+ Before upgrading the memory store, back it up. `slopie maintain --migrate` creates a pre-upgrade backup. See the [changelog](https://github.com/HulkInTherapy/slopie/blob/main/CHANGELOG.md).
59
78
 
60
79
  MIT licensed. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
@@ -1,12 +1,12 @@
1
1
  // Instant first frame for `slopie audit`.
2
2
  //
3
3
  // Loading the CLI bundle takes ~300 ms. For the interactive audit we paint the
4
- // loading story's first frame (Slopie, the brand, the privacy promise) before
5
- // the bundle loads, so the screen responds at once; the real loading story
6
- // takes over the alternate screen in exactly the same place and calls
7
- // `globalThis.__slopieSplash.handOff()`.
4
+ // loading screen's first frame (the header, the section label, the first
5
+ // caption and the privacy promise) before the bundle loads, so the screen
6
+ // responds at once; the real loading screen takes over the alternate screen
7
+ // in exactly the same place and calls `globalThis.__slopieSplash.handOff()`.
8
8
  //
9
- // The pixel art and geometry mirror packages/cli/src/audit-ui/{mascot,loading}.ts;
9
+ // The palette, text and geometry mirror packages/cli/src/audit-ui/{gfx/color,loading}.ts;
10
10
  // tests/audit-ui/splash.test.ts keeps them identical.
11
11
  //
12
12
  // If anything else ends up writing (an error, --help), the alternate screen is
@@ -15,68 +15,46 @@
15
15
 
16
16
  const PLAIN_FLAGS = new Set(['--json', '--plain', '--export', '--legacy', '--sweep', '--verify', '--help', '-h', '--version', '-V']);
17
17
 
18
- /** Slopie, idle. Rows of palette letters; '.' is transparent. First row is pixel row -2 (the tip). */
19
- export const SPLASH_ART = {
20
- big: {
21
- top: -2,
22
- palette: ['#ff7a1a', '#e16011', '#c94a0a', '#ffb070', '#fff4e6', '#ffffff', '#1c0f08', '#8a2e05', '#ff7027'],
23
- rows: ['........ab....', '.......aa.....', '.....aaaa.....', '...aaddaaaa...', '..adedaaaaaa..', '.aadaaaaaaaab.', '.adaaaaaaaabb.', 'aaaaaaaaaaaabb', 'aaafgaaaafgabb', 'aaaggaaaaggabb', 'aaaaaaaaaaaabb', 'aiiaahaahaaiib', 'caaaaahhaaaabc', 'ccaaaaaaaaaacc', '.cccccccccccc.', '..cccccccccc..', '...c.......c..', '...........b..'],
24
- },
25
- compact: {
26
- top: 0,
27
- palette: ['#ff7a1a', '#e16011', '#c94a0a', '#ffb070', '#fff4e6', '#1c0f08', '#8a2e05'],
28
- rows: ['..aaaa..', '.adaaaa.', 'aeaaaaab', 'aafaafab', 'aafaafab', 'aagaagab', 'caaggaac', '.cccccc.'],
29
- },
18
+ /** The audit palette (mirrors C in audit-ui/gfx/color.ts), with its tuned 256-colour indices. */
19
+ export const SPLASH_PALETTE = {
20
+ bg: ['#08090a', 232], text: ['#f7f8f8', 255], text2: ['#8a8f98', 245], text3: ['#5d6168', 241], rule: ['#1f2125', 234], accent: ['#7c7cff', 105],
30
21
  };
22
+ export const PRIVACY_LINE = 'all local · nothing leaves this machine';
23
+ export const FIRST_CAPTION = 'finding your agents';
31
24
 
32
25
  /** Mirrors loadingGeometry() in audit-ui/loading.ts. */
33
26
  export function splashGeometry(w, h) {
34
27
  const L = w >= 100 && h >= 30;
35
28
  const S = w < 76 || h < 23;
36
- const mx = L ? 4 : S ? 2 : 3;
37
- const scale = L ? 2 : 1;
38
- const mascotW = S ? 10 : 18 * scale;
39
- const mascotH = S ? 4 : 8 * scale;
40
- const panelX = S ? mx : Math.max(mx + mascotW + 6, Math.floor(w * (L ? 0.46 : 0.42)));
41
- const sx = S ? Math.floor((w - 10) / 2) : Math.max(mx, Math.floor((panelX - mascotW) / 2) - 1);
42
- const sy = S ? 2 : Math.max(2, Math.floor((h - mascotH) / 2) - 2);
43
- const rowsY = S ? sy + mascotH + 2 : Math.max(3, Math.floor(h / 2) - (L ? 6 : 5));
44
- return { L, S, mx, scale, mascotW, mascotH, panelX, sx, sy, rowsY };
29
+ const mx = L ? 6 : 3;
30
+ const top = L ? 1 : 0;
31
+ const footY = L ? h - 2 : h - 1;
32
+ const rowStep = L ? 2 : 1;
33
+ const numH = L ? 5 : 3;
34
+ const blockH = 2 + 4 * rowStep + 1 + numH + 2 + 2 + 1;
35
+ const labelY = Math.max(top + 3, top + 2 + Math.floor((footY - top - 2 - blockH) / 2));
36
+ const rowsY = labelY + 2;
37
+ const numY = rowsY + 4 * rowStep + 1;
38
+ const barY = numY + numH + 2;
39
+ const captionY = barY + 2;
40
+ return { L, S, mx, top, footY, rowStep, numH, labelY, rowsY, numY, barY, captionY };
45
41
  }
46
42
 
47
- const INK = [13, 11, 10];
48
- const TUNED_256 = { '#ff7a1a': 208, '#ffb070': 215, '#fff4e6': 230, '#ffffff': 231, '#c94a0a': 166, '#8a2e05': 94, '#1c0f08': 232 };
49
- const hexRgb = hex => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16));
50
- function to256(hex) {
51
- if (TUNED_256[hex]) return TUNED_256[hex];
52
- const cube = [0, 95, 135, 175, 215, 255];
53
- const near = v => cube.reduce((best, c, i) => (Math.abs(c - v) < Math.abs(cube[best] - v) ? i : best), 0);
54
- const [r, g, b] = hexRgb(hex);
55
- return 16 + 36 * near(r) + 6 * near(g) + near(b);
56
- }
57
-
58
- /** Cells for the art: Map "x,y" → [topHex|null, bottomHex|null], in cell coordinates. */
59
- export function splashCells(size, x, y, scale) {
60
- const art = SPLASH_ART[size];
61
- const offset = size === 'big' ? 2 : 0;
62
- const top = size === 'big' ? 2 : 0;
63
- const cells = new Map();
64
- art.rows.forEach((row, ry) => {
65
- for (let rx = 0; rx < row.length; rx++) {
66
- if (row[rx] === '.') continue;
67
- const color = art.palette['abcdefghij'.indexOf(row[rx])];
68
- const px = rx, py = ry + art.top;
69
- for (let sy = 0; sy < scale; sy++) for (let sx = 0; sx < scale; sx++) {
70
- const cx = x + (px + offset) * scale + sx;
71
- const pyy = y * 2 + (py + top) * scale + sy;
72
- const key = `${cx},${pyy >> 1}`;
73
- const cell = cells.get(key) ?? [null, null];
74
- cell[pyy & 1] = color;
75
- cells.set(key, cell);
76
- }
77
- }
78
- });
79
- return cells;
43
+ /** The splash as text runs: { x, y, text, tone, bold }. */
44
+ export function splashRuns(w, h, unicode = true) {
45
+ const g = splashGeometry(w, h);
46
+ const iw = w - g.mx * 2;
47
+ const runs = [
48
+ { x: g.mx, y: g.top, text: 'slopie', tone: 'text', bold: true },
49
+ { x: g.mx + 7, y: g.top, text: 'audit', tone: 'text3' },
50
+ { x: g.mx, y: g.top + 1, text: (unicode ? '─' : '-').repeat(iw), tone: 'rule' },
51
+ { x: g.mx, y: g.labelY, text: 'YOUR AGENTS', tone: 'text3' },
52
+ { x: g.mx, y: g.barY, text: (unicode ? '─' : '-').repeat(Math.min(64, iw)), tone: 'rule' },
53
+ { x: g.mx, y: g.captionY, text: FIRST_CAPTION, tone: 'text2' },
54
+ { x: g.mx, y: g.footY, text: unicode ? '●' : '*', tone: 'accent' },
55
+ { x: g.mx + 2, y: g.footY, text: PRIVACY_LINE, tone: 'text3' },
56
+ ];
57
+ return runs;
80
58
  }
81
59
 
82
60
  export function auditSplash(argv = process.argv.slice(2)) {
@@ -90,32 +68,14 @@ export function auditSplash(argv = process.argv.slice(2)) {
90
68
  const color = !('NO_COLOR' in process.env) && !argv.includes('--no-color') && process.env.TERM !== 'dumb';
91
69
  const truecolor = /^(truecolor|24bit)$/i.test(process.env.COLORTERM ?? '') || /^(iTerm\.app|WezTerm|ghostty|vscode)$/i.test(process.env.TERM_PROGRAM ?? '');
92
70
  const deep = color && (truecolor || /256/.test(process.env.TERM ?? '') || process.env.TERM_PROGRAM === 'Apple_Terminal');
93
- const fg = hex => (!color ? '' : truecolor ? `\x1b[38;2;${hexRgb(hex).join(';')}m` : `\x1b[38;5;${to256(hex)}m`);
94
- const bgOf = hex => (!deep ? '' : truecolor ? `\x1b[48;2;${hexRgb(hex).join(';')}m` : `\x1b[48;5;${to256(hex)}m`);
95
- const ink = !deep ? '' : truecolor ? `\x1b[48;2;${INK.join(';')}m` : '\x1b[48;5;233m';
71
+ const rgb = hex => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16)).join(';');
72
+ const fg = tone => (!deep ? '' : truecolor ? `\x1b[38;2;${rgb(SPLASH_PALETTE[tone][0])}m` : `\x1b[38;5;${SPLASH_PALETTE[tone][1]}m`);
73
+ const ink = !deep ? '' : truecolor ? `\x1b[48;2;${rgb(SPLASH_PALETTE.bg[0])}m` : `\x1b[48;5;${SPLASH_PALETTE.bg[1]}m`;
96
74
  const reset = `\x1b[0m${ink}`;
75
+ const unicode = !argv.includes('--ascii');
97
76
 
98
- const g = splashGeometry(w, h);
99
77
  let out = `\x1b[?1049h\x1b[?25l${ink}\x1b[H\x1b[2J`;
100
- const at = (x, y, text) => { out += `\x1b[${y + 1};${x + 1}H${text}${reset}`; };
101
- at(g.mx, g.L ? 1 : 0, `\x1b[1m${fg('#ff7a1a')}slopie${reset} ${fg('#5c5c5c')}audit`);
102
- if (deep) {
103
- const cells = splashCells(g.S ? 'compact' : 'big', g.sx, g.sy, g.S ? 1 : g.scale);
104
- for (const [key, [top, bottom]] of cells) {
105
- const [x, y] = key.split(',').map(Number);
106
- if (top && bottom) at(x, y, top === bottom ? `${fg(top)}█` : `${fg(top)}${bgOf(bottom)}▀`);
107
- else if (top) at(x, y, `${fg(top)}▀`);
108
- else if (bottom) at(x, y, `${fg(bottom)}▄`);
109
- }
110
- } else {
111
- at(g.sx + 4, g.sy + Math.floor(g.mascotH / 2), `${fg('#ff7a1a')}(•ᴗ•)`);
112
- }
113
- const privacy = 'all local · nothing leaves this machine';
114
- const py = h - (g.L ? 2 : 1);
115
- const px = Math.round(w / 2 - (privacy.length + 2) / 2);
116
- at(px, py, `${fg('#ff7a1a')}●${reset} ${fg('#5c5c5c')}${privacy}`);
117
- const cy = g.rowsY + (g.L ? 13 : 9);
118
- at(g.panelX, cy, `${fg('#ffb070')}finding your agents`);
78
+ for (const run of splashRuns(w, h, unicode)) out += `\x1b[${run.y + 1};${run.x + 1}H${run.bold ? '\x1b[1m' : ''}${fg(run.tone)}${run.text}${reset}`;
119
79
  stdout.write(out + '\x1b[0m');
120
80
  try { stdin.setRawMode(true); } catch { /* not a raw-capable tty */ }
121
81