volute 0.4.0 → 0.6.0

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.
Files changed (82) hide show
  1. package/README.md +22 -22
  2. package/dist/agent-X7GJLBLW.js +79 -0
  3. package/dist/{agent-manager-AUCKMGPR.js → agent-manager-JDVXU3ON.js} +4 -4
  4. package/dist/channel-SMCNOIVQ.js +262 -0
  5. package/dist/chunk-AOKAQGO4.js +107 -0
  6. package/dist/{chunk-VRVVQIYY.js → chunk-AZEL2IEK.js} +1 -1
  7. package/dist/chunk-B3R6L2GW.js +24 -0
  8. package/dist/{chunk-MXUCNIBG.js → chunk-BX7KI4S3.js} +68 -3
  9. package/dist/{chunk-I6OHXCMV.js → chunk-G6ZNGLUX.js} +47 -9
  10. package/dist/{chunk-DNOXHLE5.js → chunk-H7AMDUIA.js} +1 -1
  11. package/dist/{chunk-YGFIWIOF.js → chunk-JR4UXCTO.js} +1 -1
  12. package/dist/{chunk-3C2XR4IY.js → chunk-UWHWAPGO.js} +120 -107
  13. package/dist/{chunk-SOZA2TLP.js → chunk-W76KWE23.js} +1 -1
  14. package/dist/{chunk-GSPKUPKU.js → chunk-XUA3JUFK.js} +2 -1
  15. package/dist/chunk-ZYGKG6VC.js +22 -0
  16. package/dist/chunk-ZZOOTYXK.js +583 -0
  17. package/dist/cli.js +83 -74
  18. package/dist/{connector-DKDJTLYZ.js → connector-Y7JPNROO.js} +11 -6
  19. package/dist/connectors/discord.js +34 -5
  20. package/dist/connectors/slack.js +36 -8
  21. package/dist/connectors/telegram.js +55 -6
  22. package/dist/create-G525LWEA.js +91 -0
  23. package/dist/{daemon-client-XR24PUJF.js → daemon-client-442IV43D.js} +2 -2
  24. package/dist/daemon.js +1273 -384
  25. package/dist/{delete-55MXCEY5.js → delete-2PH2CGDY.js} +7 -8
  26. package/dist/{down-3OB6UVAJ.js → down-FXWAN66A.js} +1 -1
  27. package/dist/{env-JB27UAC3.js → env-7GLUJCWS.js} +8 -5
  28. package/dist/{history-BKG74I43.js → history-H72ZUIBN.js} +3 -3
  29. package/dist/{import-4CI2ZUTJ.js → import-AVKQJDYC.js} +8 -8
  30. package/dist/{logs-NXFFGUKY.js → logs-EDGK26AK.js} +2 -2
  31. package/dist/message-SCOQDR3P.js +32 -0
  32. package/dist/{package-Z2SFO2SV.js → package-4DP4Y4UO.js} +1 -1
  33. package/dist/restart-O4ETYLJF.js +29 -0
  34. package/dist/{schedule-A35SH4HT.js → schedule-S6QVC5ON.js} +10 -5
  35. package/dist/send-G7PE4DOJ.js +72 -0
  36. package/dist/{setup-2FDVN7OF.js → setup-F4TCWVSP.js} +5 -5
  37. package/dist/{start-LDPMCMYT.js → start-VHQ7LNWM.js} +3 -3
  38. package/dist/{status-MVSQG54T.js → status-QAJWXKMZ.js} +3 -3
  39. package/dist/{stop-5PZTZCLL.js → stop-CAGCT5NI.js} +6 -7
  40. package/dist/{up-F7TMTLRE.js → up-CSX3ZUIU.js} +16 -4
  41. package/dist/update-XSIX3GGP.js +140 -0
  42. package/dist/update-check-5ZADDHCK.js +17 -0
  43. package/dist/{upgrade-6ZW2RD64.js → upgrade-YXKPWDRU.js} +16 -15
  44. package/dist/{variant-T64BKARF.js → variant-4Z6W3PP6.js} +15 -10
  45. package/dist/web-assets/assets/index-D5PzIndO.js +308 -0
  46. package/dist/web-assets/index.html +2 -2
  47. package/drizzle/0003_clean_ego.sql +12 -0
  48. package/drizzle/meta/0003_snapshot.json +417 -0
  49. package/drizzle/meta/_journal.json +7 -0
  50. package/package.json +1 -1
  51. package/templates/_base/.init/.config/hooks/startup-context.sh +19 -1
  52. package/templates/_base/.init/.config/scripts/session-reader.ts +59 -0
  53. package/templates/_base/_skills/sessions/SKILL.md +49 -0
  54. package/templates/_base/_skills/volute-agent/SKILL.md +114 -14
  55. package/templates/_base/home/.config/routes.json +10 -0
  56. package/templates/_base/home/VOLUTE.md +14 -35
  57. package/templates/_base/src/lib/format-prefix.ts +7 -1
  58. package/templates/_base/src/lib/router.ts +193 -19
  59. package/templates/_base/src/lib/routing.ts +55 -18
  60. package/templates/_base/src/lib/session-monitor.ts +400 -0
  61. package/templates/_base/src/lib/types.ts +5 -1
  62. package/templates/agent-sdk/.init/.config/routes.json +5 -0
  63. package/templates/agent-sdk/.init/CLAUDE.md +2 -2
  64. package/templates/agent-sdk/src/agent.ts +18 -1
  65. package/templates/agent-sdk/src/lib/hooks/session-context.ts +32 -0
  66. package/templates/agent-sdk/src/server.ts +8 -2
  67. package/templates/agent-sdk/volute-template.json +1 -1
  68. package/templates/pi/.init/.config/routes.json +5 -0
  69. package/templates/pi/.init/AGENTS.md +1 -1
  70. package/templates/pi/src/agent.ts +12 -4
  71. package/templates/pi/src/lib/session-context-extension.ts +33 -0
  72. package/templates/pi/src/server.ts +1 -1
  73. package/templates/pi/volute-template.json +1 -1
  74. package/dist/channel-DQ6UY7QB.js +0 -67
  75. package/dist/chunk-5OCWMTVS.js +0 -152
  76. package/dist/chunk-ZHCE4DPY.js +0 -110
  77. package/dist/create-ILVOG75A.js +0 -79
  78. package/dist/send-3U6OTKG7.js +0 -57
  79. package/dist/web-assets/assets/index-NS621maO.js +0 -296
  80. package/templates/agent-sdk/.init/.config/sessions.json +0 -4
  81. package/templates/pi/.init/.config/sessions.json +0 -1
  82. package/dist/{service-SA4TTMDU.js → service-HZNIDNJF.js} +3 -3
@@ -6,8 +6,8 @@
6
6
  <title>volute</title>
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com" />
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
- <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,300;0,400;0,500;0,600;1,400&family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-NS621maO.js"></script>
9
+ <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap" rel="stylesheet" />
10
+ <script type="module" crossorigin src="/assets/index-D5PzIndO.js"></script>
11
11
  </head>
12
12
  <body>
13
13
  <div id="root"></div>
@@ -0,0 +1,12 @@
1
+ CREATE TABLE `conversation_participants` (
2
+ `conversation_id` text NOT NULL,
3
+ `user_id` integer NOT NULL,
4
+ `role` text DEFAULT 'member' NOT NULL,
5
+ `joined_at` text DEFAULT (datetime('now')) NOT NULL,
6
+ FOREIGN KEY (`conversation_id`) REFERENCES `conversations`(`id`) ON UPDATE no action ON DELETE cascade,
7
+ FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade
8
+ );
9
+ --> statement-breakpoint
10
+ CREATE UNIQUE INDEX `idx_cp_unique` ON `conversation_participants` (`conversation_id`,`user_id`);--> statement-breakpoint
11
+ CREATE INDEX `idx_cp_user_id` ON `conversation_participants` (`user_id`);--> statement-breakpoint
12
+ ALTER TABLE `users` ADD `user_type` text DEFAULT 'human' NOT NULL;
@@ -0,0 +1,417 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "sqlite",
4
+ "id": "b96d5915-4500-42f4-8638-28f0526d41f0",
5
+ "prevId": "b76b7e15-3489-4a1d-ac23-977cf267d174",
6
+ "tables": {
7
+ "agent_messages": {
8
+ "name": "agent_messages",
9
+ "columns": {
10
+ "id": {
11
+ "name": "id",
12
+ "type": "integer",
13
+ "primaryKey": true,
14
+ "notNull": true,
15
+ "autoincrement": true
16
+ },
17
+ "agent": {
18
+ "name": "agent",
19
+ "type": "text",
20
+ "primaryKey": false,
21
+ "notNull": true,
22
+ "autoincrement": false
23
+ },
24
+ "channel": {
25
+ "name": "channel",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true,
29
+ "autoincrement": false
30
+ },
31
+ "role": {
32
+ "name": "role",
33
+ "type": "text",
34
+ "primaryKey": false,
35
+ "notNull": true,
36
+ "autoincrement": false
37
+ },
38
+ "sender": {
39
+ "name": "sender",
40
+ "type": "text",
41
+ "primaryKey": false,
42
+ "notNull": false,
43
+ "autoincrement": false
44
+ },
45
+ "content": {
46
+ "name": "content",
47
+ "type": "text",
48
+ "primaryKey": false,
49
+ "notNull": true,
50
+ "autoincrement": false
51
+ },
52
+ "created_at": {
53
+ "name": "created_at",
54
+ "type": "text",
55
+ "primaryKey": false,
56
+ "notNull": true,
57
+ "autoincrement": false,
58
+ "default": "(datetime('now'))"
59
+ }
60
+ },
61
+ "indexes": {
62
+ "idx_agent_messages_agent": {
63
+ "name": "idx_agent_messages_agent",
64
+ "columns": ["agent"],
65
+ "isUnique": false
66
+ },
67
+ "idx_agent_messages_channel": {
68
+ "name": "idx_agent_messages_channel",
69
+ "columns": ["agent", "channel"],
70
+ "isUnique": false
71
+ }
72
+ },
73
+ "foreignKeys": {},
74
+ "compositePrimaryKeys": {},
75
+ "uniqueConstraints": {},
76
+ "checkConstraints": {}
77
+ },
78
+ "conversation_participants": {
79
+ "name": "conversation_participants",
80
+ "columns": {
81
+ "conversation_id": {
82
+ "name": "conversation_id",
83
+ "type": "text",
84
+ "primaryKey": false,
85
+ "notNull": true,
86
+ "autoincrement": false
87
+ },
88
+ "user_id": {
89
+ "name": "user_id",
90
+ "type": "integer",
91
+ "primaryKey": false,
92
+ "notNull": true,
93
+ "autoincrement": false
94
+ },
95
+ "role": {
96
+ "name": "role",
97
+ "type": "text",
98
+ "primaryKey": false,
99
+ "notNull": true,
100
+ "autoincrement": false,
101
+ "default": "'member'"
102
+ },
103
+ "joined_at": {
104
+ "name": "joined_at",
105
+ "type": "text",
106
+ "primaryKey": false,
107
+ "notNull": true,
108
+ "autoincrement": false,
109
+ "default": "(datetime('now'))"
110
+ }
111
+ },
112
+ "indexes": {
113
+ "idx_cp_unique": {
114
+ "name": "idx_cp_unique",
115
+ "columns": ["conversation_id", "user_id"],
116
+ "isUnique": true
117
+ },
118
+ "idx_cp_user_id": {
119
+ "name": "idx_cp_user_id",
120
+ "columns": ["user_id"],
121
+ "isUnique": false
122
+ }
123
+ },
124
+ "foreignKeys": {
125
+ "conversation_participants_conversation_id_conversations_id_fk": {
126
+ "name": "conversation_participants_conversation_id_conversations_id_fk",
127
+ "tableFrom": "conversation_participants",
128
+ "tableTo": "conversations",
129
+ "columnsFrom": ["conversation_id"],
130
+ "columnsTo": ["id"],
131
+ "onDelete": "cascade",
132
+ "onUpdate": "no action"
133
+ },
134
+ "conversation_participants_user_id_users_id_fk": {
135
+ "name": "conversation_participants_user_id_users_id_fk",
136
+ "tableFrom": "conversation_participants",
137
+ "tableTo": "users",
138
+ "columnsFrom": ["user_id"],
139
+ "columnsTo": ["id"],
140
+ "onDelete": "cascade",
141
+ "onUpdate": "no action"
142
+ }
143
+ },
144
+ "compositePrimaryKeys": {},
145
+ "uniqueConstraints": {},
146
+ "checkConstraints": {}
147
+ },
148
+ "conversations": {
149
+ "name": "conversations",
150
+ "columns": {
151
+ "id": {
152
+ "name": "id",
153
+ "type": "text",
154
+ "primaryKey": true,
155
+ "notNull": true,
156
+ "autoincrement": false
157
+ },
158
+ "agent_name": {
159
+ "name": "agent_name",
160
+ "type": "text",
161
+ "primaryKey": false,
162
+ "notNull": true,
163
+ "autoincrement": false
164
+ },
165
+ "channel": {
166
+ "name": "channel",
167
+ "type": "text",
168
+ "primaryKey": false,
169
+ "notNull": true,
170
+ "autoincrement": false
171
+ },
172
+ "user_id": {
173
+ "name": "user_id",
174
+ "type": "integer",
175
+ "primaryKey": false,
176
+ "notNull": false,
177
+ "autoincrement": false
178
+ },
179
+ "title": {
180
+ "name": "title",
181
+ "type": "text",
182
+ "primaryKey": false,
183
+ "notNull": false,
184
+ "autoincrement": false
185
+ },
186
+ "created_at": {
187
+ "name": "created_at",
188
+ "type": "text",
189
+ "primaryKey": false,
190
+ "notNull": true,
191
+ "autoincrement": false,
192
+ "default": "(datetime('now'))"
193
+ },
194
+ "updated_at": {
195
+ "name": "updated_at",
196
+ "type": "text",
197
+ "primaryKey": false,
198
+ "notNull": true,
199
+ "autoincrement": false,
200
+ "default": "(datetime('now'))"
201
+ }
202
+ },
203
+ "indexes": {
204
+ "idx_conversations_agent_name": {
205
+ "name": "idx_conversations_agent_name",
206
+ "columns": ["agent_name"],
207
+ "isUnique": false
208
+ },
209
+ "idx_conversations_user_id": {
210
+ "name": "idx_conversations_user_id",
211
+ "columns": ["user_id"],
212
+ "isUnique": false
213
+ },
214
+ "idx_conversations_updated_at": {
215
+ "name": "idx_conversations_updated_at",
216
+ "columns": ["updated_at"],
217
+ "isUnique": false
218
+ }
219
+ },
220
+ "foreignKeys": {
221
+ "conversations_user_id_users_id_fk": {
222
+ "name": "conversations_user_id_users_id_fk",
223
+ "tableFrom": "conversations",
224
+ "tableTo": "users",
225
+ "columnsFrom": ["user_id"],
226
+ "columnsTo": ["id"],
227
+ "onDelete": "no action",
228
+ "onUpdate": "no action"
229
+ }
230
+ },
231
+ "compositePrimaryKeys": {},
232
+ "uniqueConstraints": {},
233
+ "checkConstraints": {}
234
+ },
235
+ "messages": {
236
+ "name": "messages",
237
+ "columns": {
238
+ "id": {
239
+ "name": "id",
240
+ "type": "integer",
241
+ "primaryKey": true,
242
+ "notNull": true,
243
+ "autoincrement": true
244
+ },
245
+ "conversation_id": {
246
+ "name": "conversation_id",
247
+ "type": "text",
248
+ "primaryKey": false,
249
+ "notNull": true,
250
+ "autoincrement": false
251
+ },
252
+ "role": {
253
+ "name": "role",
254
+ "type": "text",
255
+ "primaryKey": false,
256
+ "notNull": true,
257
+ "autoincrement": false
258
+ },
259
+ "sender_name": {
260
+ "name": "sender_name",
261
+ "type": "text",
262
+ "primaryKey": false,
263
+ "notNull": false,
264
+ "autoincrement": false
265
+ },
266
+ "content": {
267
+ "name": "content",
268
+ "type": "text",
269
+ "primaryKey": false,
270
+ "notNull": true,
271
+ "autoincrement": false
272
+ },
273
+ "created_at": {
274
+ "name": "created_at",
275
+ "type": "text",
276
+ "primaryKey": false,
277
+ "notNull": true,
278
+ "autoincrement": false,
279
+ "default": "(datetime('now'))"
280
+ }
281
+ },
282
+ "indexes": {
283
+ "idx_messages_conversation_id": {
284
+ "name": "idx_messages_conversation_id",
285
+ "columns": ["conversation_id"],
286
+ "isUnique": false
287
+ }
288
+ },
289
+ "foreignKeys": {
290
+ "messages_conversation_id_conversations_id_fk": {
291
+ "name": "messages_conversation_id_conversations_id_fk",
292
+ "tableFrom": "messages",
293
+ "tableTo": "conversations",
294
+ "columnsFrom": ["conversation_id"],
295
+ "columnsTo": ["id"],
296
+ "onDelete": "cascade",
297
+ "onUpdate": "no action"
298
+ }
299
+ },
300
+ "compositePrimaryKeys": {},
301
+ "uniqueConstraints": {},
302
+ "checkConstraints": {}
303
+ },
304
+ "sessions": {
305
+ "name": "sessions",
306
+ "columns": {
307
+ "id": {
308
+ "name": "id",
309
+ "type": "text",
310
+ "primaryKey": true,
311
+ "notNull": true,
312
+ "autoincrement": false
313
+ },
314
+ "user_id": {
315
+ "name": "user_id",
316
+ "type": "integer",
317
+ "primaryKey": false,
318
+ "notNull": true,
319
+ "autoincrement": false
320
+ },
321
+ "created_at": {
322
+ "name": "created_at",
323
+ "type": "integer",
324
+ "primaryKey": false,
325
+ "notNull": true,
326
+ "autoincrement": false
327
+ }
328
+ },
329
+ "indexes": {},
330
+ "foreignKeys": {
331
+ "sessions_user_id_users_id_fk": {
332
+ "name": "sessions_user_id_users_id_fk",
333
+ "tableFrom": "sessions",
334
+ "tableTo": "users",
335
+ "columnsFrom": ["user_id"],
336
+ "columnsTo": ["id"],
337
+ "onDelete": "cascade",
338
+ "onUpdate": "no action"
339
+ }
340
+ },
341
+ "compositePrimaryKeys": {},
342
+ "uniqueConstraints": {},
343
+ "checkConstraints": {}
344
+ },
345
+ "users": {
346
+ "name": "users",
347
+ "columns": {
348
+ "id": {
349
+ "name": "id",
350
+ "type": "integer",
351
+ "primaryKey": true,
352
+ "notNull": true,
353
+ "autoincrement": true
354
+ },
355
+ "username": {
356
+ "name": "username",
357
+ "type": "text",
358
+ "primaryKey": false,
359
+ "notNull": true,
360
+ "autoincrement": false
361
+ },
362
+ "password_hash": {
363
+ "name": "password_hash",
364
+ "type": "text",
365
+ "primaryKey": false,
366
+ "notNull": true,
367
+ "autoincrement": false
368
+ },
369
+ "role": {
370
+ "name": "role",
371
+ "type": "text",
372
+ "primaryKey": false,
373
+ "notNull": true,
374
+ "autoincrement": false,
375
+ "default": "'pending'"
376
+ },
377
+ "user_type": {
378
+ "name": "user_type",
379
+ "type": "text",
380
+ "primaryKey": false,
381
+ "notNull": true,
382
+ "autoincrement": false,
383
+ "default": "'human'"
384
+ },
385
+ "created_at": {
386
+ "name": "created_at",
387
+ "type": "text",
388
+ "primaryKey": false,
389
+ "notNull": true,
390
+ "autoincrement": false,
391
+ "default": "(datetime('now'))"
392
+ }
393
+ },
394
+ "indexes": {
395
+ "users_username_unique": {
396
+ "name": "users_username_unique",
397
+ "columns": ["username"],
398
+ "isUnique": true
399
+ }
400
+ },
401
+ "foreignKeys": {},
402
+ "compositePrimaryKeys": {},
403
+ "uniqueConstraints": {},
404
+ "checkConstraints": {}
405
+ }
406
+ },
407
+ "views": {},
408
+ "enums": {},
409
+ "_meta": {
410
+ "schemas": {},
411
+ "tables": {},
412
+ "columns": {}
413
+ },
414
+ "internal": {
415
+ "indexes": {}
416
+ }
417
+ }
@@ -22,6 +22,13 @@
22
22
  "when": 1770672071268,
23
23
  "tag": "0002_wealthy_the_call",
24
24
  "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "6",
29
+ "when": 1770770603615,
30
+ "tag": "0003_clean_ego",
31
+ "breakpoints": true
25
32
  }
26
33
  ]
27
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volute",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "CLI for creating and managing self-modifying AI agents powered by the Claude Agent SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -10,7 +10,16 @@ SOURCE=$(echo "$INPUT" | jq -r '.source // "startup"')
10
10
 
11
11
  CONTEXT="Session ${SOURCE} at $(date '+%Y-%m-%d %H:%M')."
12
12
 
13
- # Add recent journal context if available
13
+ # Active sessions
14
+ SESSIONS_DIR=".volute/sessions"
15
+ if [ -d "$SESSIONS_DIR" ]; then
16
+ SESSION_LIST=$(ls -1 "$SESSIONS_DIR"/*.json 2>/dev/null | xargs -I{} basename {} .json | sort)
17
+ if [ -n "$SESSION_LIST" ]; then
18
+ CONTEXT="$CONTEXT Active sessions: $(echo "$SESSION_LIST" | tr '\n' ', ' | sed 's/, $//')."
19
+ fi
20
+ fi
21
+
22
+ # Last journal entry
14
23
  JOURNAL_DIR="home/memory/journal"
15
24
  if [ -d "$JOURNAL_DIR" ]; then
16
25
  LATEST=$(ls -1 "$JOURNAL_DIR"/*.md 2>/dev/null | sort | tail -1)
@@ -19,6 +28,15 @@ if [ -d "$JOURNAL_DIR" ]; then
19
28
  fi
20
29
  fi
21
30
 
31
+ # Pending channel invites
32
+ INBOX_DIR="home/inbox"
33
+ if [ -d "$INBOX_DIR" ]; then
34
+ INVITE_COUNT=$(ls -1 "$INBOX_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ')
35
+ if [ "$INVITE_COUNT" -gt 0 ] 2>/dev/null; then
36
+ CONTEXT="$CONTEXT Pending channel invites: ${INVITE_COUNT} (check inbox/)."
37
+ fi
38
+ fi
39
+
22
40
  # Output in SessionStart hook format
23
41
  jq -n --arg ctx "$CONTEXT" '{
24
42
  hookSpecificOutput: {
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Session reader — displays a human-readable log of another session's activity.
4
+ *
5
+ * Usage: npx tsx .config/scripts/session-reader.ts <session-name> [--lines N]
6
+ *
7
+ * Runs from the agent's home/ directory.
8
+ */
9
+ import { existsSync } from "node:fs";
10
+ import { resolve } from "node:path";
11
+ import {
12
+ readSessionLog,
13
+ resolveAgentSdkJsonl,
14
+ resolvePiJsonl,
15
+ } from "../../src/lib/session-monitor.js";
16
+
17
+ const args = process.argv.slice(2);
18
+ let sessionName: string | undefined;
19
+ let lines = 50;
20
+
21
+ for (let i = 0; i < args.length; i++) {
22
+ if (args[i] === "--lines" && args[i + 1]) {
23
+ lines = parseInt(args[++i], 10);
24
+ } else if (!sessionName && !args[i].startsWith("-")) {
25
+ sessionName = args[i];
26
+ }
27
+ }
28
+
29
+ if (!sessionName) {
30
+ console.error("Usage: npx tsx .config/scripts/session-reader.ts <session-name> [--lines N]");
31
+ process.exit(1);
32
+ }
33
+
34
+ // Detect template type and resolve JSONL path
35
+ const cwd = process.cwd();
36
+ const agentSdkSessions = resolve(cwd, "../.volute/sessions");
37
+ const piSessions = resolve(cwd, "../.volute/pi-sessions");
38
+
39
+ let jsonlPath: string | null = null;
40
+ let format: "agent-sdk" | "pi";
41
+
42
+ if (existsSync(agentSdkSessions)) {
43
+ format = "agent-sdk";
44
+ jsonlPath = resolveAgentSdkJsonl(agentSdkSessions, sessionName, cwd);
45
+ } else if (existsSync(piSessions)) {
46
+ format = "pi";
47
+ jsonlPath = resolvePiJsonl(piSessions, sessionName);
48
+ } else {
49
+ console.error("No session directory found. Expected .volute/sessions/ or .volute/pi-sessions/");
50
+ process.exit(1);
51
+ }
52
+
53
+ if (!jsonlPath || !existsSync(jsonlPath)) {
54
+ console.error(`No session log found for "${sessionName}".`);
55
+ process.exit(1);
56
+ }
57
+
58
+ const output = readSessionLog({ jsonlPath, format, lines });
59
+ console.log(output);
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: Sessions
3
+ description: This skill should be used when checking activity in other sessions, reading session logs, understanding cross-session context, or investigating what happened in another session. Covers "session activity", "other sessions", "session reader", "session log", "cross-session", "what happened in discord", "check session".
4
+ ---
5
+
6
+ # Cross-Session Awareness
7
+
8
+ You can have multiple concurrent sessions (main, discord, email, etc.), each with its own conversation history stored as a JSONL file.
9
+
10
+ ## Automatic Updates
11
+
12
+ When a message arrives, you automatically receive a brief summary of new activity in other sessions (if any). This appears as a `[Session Activity]` block showing what happened since your last check.
13
+
14
+ ## Listing Sessions
15
+
16
+ To see which sessions are active:
17
+
18
+ ```sh
19
+ ls ../.volute/sessions/ # agent-sdk template
20
+ ls ../.volute/pi-sessions/ # pi template
21
+ ```
22
+
23
+ ## Reading a Session Log
24
+
25
+ For a detailed view of what happened in another session, run the session reader script:
26
+
27
+ ```sh
28
+ npx tsx .config/scripts/session-reader.ts <session-name> [--lines N]
29
+ ```
30
+
31
+ - `session-name`: The session to inspect (e.g., `discord`, `main`, `email`)
32
+ - `--lines N`: Number of recent entries to show (default: 50)
33
+
34
+ ### Output Format
35
+
36
+ The reader shows a chronological log with:
37
+ - **User messages**: Full text of what was sent
38
+ - **Assistant text**: Full text of your responses
39
+ - **Tool uses**: `[ToolName primary-arg]` format (e.g., `[Edit home/MEMORY.md]`, `[Bash npm test]`)
40
+ - **Timestamps** on each entry
41
+
42
+ Thinking blocks, tool result content, and metadata entries are omitted for readability.
43
+
44
+ ## When to Use This
45
+
46
+ - When you receive a `[Session Activity]` summary and want more detail
47
+ - When you want to understand what happened in another session before responding
48
+ - When coordinating work across multiple sessions
49
+ - When a user references something from a different channel