tomo-ai 0.1.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.
- package/README.md +198 -0
- package/defaults/AGENT.md +27 -0
- package/defaults/IDENTITY.md +27 -0
- package/defaults/SOUL.md +34 -0
- package/defaults/skills/cron/SKILL.md +68 -0
- package/dist/agent.d.ts +25 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +334 -0
- package/dist/agent.js.map +1 -0
- package/dist/channels/index.d.ts +3 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +2 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/telegram.d.ts +22 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +189 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/types.d.ts +53 -0
- package/dist/channels/types.d.ts.map +1 -0
- package/dist/channels/types.js +2 -0
- package/dist/channels/types.js.map +1 -0
- package/dist/cli/cron.d.ts +3 -0
- package/dist/cli/cron.d.ts.map +1 -0
- package/dist/cli/cron.js +87 -0
- package/dist/cli/cron.js.map +1 -0
- package/dist/cli/daemon.d.ts +6 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +100 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +246 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/sessions.d.ts +3 -0
- package/dist/cli/sessions.d.ts.map +1 -0
- package/dist/cli/sessions.js +81 -0
- package/dist/cli/sessions.js.map +1 -0
- package/dist/cli/start.d.ts +3 -0
- package/dist/cli/start.d.ts.map +1 -0
- package/dist/cli/start.js +86 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +37 -0
- package/dist/config.js.map +1 -0
- package/dist/cron/scheduler.d.ts +12 -0
- package/dist/cron/scheduler.d.ts.map +1 -0
- package/dist/cron/scheduler.js +48 -0
- package/dist/cron/scheduler.js.map +1 -0
- package/dist/cron/store.d.ts +24 -0
- package/dist/cron/store.d.ts.map +1 -0
- package/dist/cron/store.js +143 -0
- package/dist/cron/store.js.map +1 -0
- package/dist/cron/types.d.ts +35 -0
- package/dist/cron/types.d.ts.map +1 -0
- package/dist/cron/types.js +2 -0
- package/dist/cron/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +26 -0
- package/dist/logger.js.map +1 -0
- package/dist/sessions/index.d.ts +3 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +2 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/store.d.ts +36 -0
- package/dist/sessions/store.d.ts.map +1 -0
- package/dist/sessions/store.js +216 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/sessions/types.d.ts +32 -0
- package/dist/sessions/types.d.ts.map +1 -0
- package/dist/sessions/types.js +2 -0
- package/dist/sessions/types.js.map +1 -0
- package/dist/skills/sync.d.ts +12 -0
- package/dist/skills/sync.d.ts.map +1 -0
- package/dist/skills/sync.js +71 -0
- package/dist/skills/sync.js.map +1 -0
- package/dist/workspace/index.d.ts +2 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +99 -0
- package/dist/workspace/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { mkdirSync, appendFileSync, readFileSync, writeFileSync, existsSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { log } from "../logger.js";
|
|
5
|
+
const UNLINKED_TTL_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
6
|
+
/** Where the SDK stores its JSONL session files */
|
|
7
|
+
function getSdkSessionDir() {
|
|
8
|
+
const home = homedir();
|
|
9
|
+
const workspacePath = join(home, ".tomo", "workspace");
|
|
10
|
+
const encoded = workspacePath.replace(/\//g, "-");
|
|
11
|
+
return join(home, ".claude", "projects", encoded);
|
|
12
|
+
}
|
|
13
|
+
export class SessionStore {
|
|
14
|
+
sessions = new Map();
|
|
15
|
+
registry = [];
|
|
16
|
+
dir;
|
|
17
|
+
historyLimit;
|
|
18
|
+
constructor(dir, historyLimit) {
|
|
19
|
+
this.dir = dir;
|
|
20
|
+
this.historyLimit = historyLimit;
|
|
21
|
+
mkdirSync(dir, { recursive: true });
|
|
22
|
+
this.loadRegistry();
|
|
23
|
+
this.cleanupExpired();
|
|
24
|
+
}
|
|
25
|
+
/** Get or create a session, loading from disk on first access */
|
|
26
|
+
get(key) {
|
|
27
|
+
let session = this.sessions.get(key);
|
|
28
|
+
if (session)
|
|
29
|
+
return session;
|
|
30
|
+
const messages = this.loadTranscript(key);
|
|
31
|
+
session = {
|
|
32
|
+
key,
|
|
33
|
+
messages,
|
|
34
|
+
createdAt: messages.length > 0 ? messages[0].timestamp : Date.now(),
|
|
35
|
+
updatedAt: messages.length > 0 ? messages[messages.length - 1].timestamp : Date.now(),
|
|
36
|
+
};
|
|
37
|
+
this.sessions.set(key, session);
|
|
38
|
+
return session;
|
|
39
|
+
}
|
|
40
|
+
/** Append a message to the session and persist to disk */
|
|
41
|
+
append(key, message) {
|
|
42
|
+
const session = this.get(key);
|
|
43
|
+
session.messages.push(message);
|
|
44
|
+
session.updatedAt = message.timestamp;
|
|
45
|
+
const file = this.transcriptPath(key);
|
|
46
|
+
appendFileSync(file, JSON.stringify(message) + "\n");
|
|
47
|
+
}
|
|
48
|
+
/** Get the last N turns of conversation for LLM context */
|
|
49
|
+
getHistory(key) {
|
|
50
|
+
const session = this.get(key);
|
|
51
|
+
const messages = session.messages;
|
|
52
|
+
if (this.historyLimit <= 0)
|
|
53
|
+
return messages;
|
|
54
|
+
let userTurns = 0;
|
|
55
|
+
let cutoff = messages.length;
|
|
56
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
57
|
+
if (messages[i].role === "user") {
|
|
58
|
+
userTurns++;
|
|
59
|
+
if (userTurns > this.historyLimit)
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
cutoff = i;
|
|
63
|
+
}
|
|
64
|
+
return messages.slice(cutoff);
|
|
65
|
+
}
|
|
66
|
+
// --- SDK Session Registry ---
|
|
67
|
+
/** Get the active SDK session ID for a channel key */
|
|
68
|
+
getSdkSessionId(key) {
|
|
69
|
+
// Re-read from disk to pick up external changes (e.g. `tomo sessions clear`)
|
|
70
|
+
this.loadRegistry();
|
|
71
|
+
const entry = this.registry.find((e) => e.channelKey === key && e.unlinkedAt === null);
|
|
72
|
+
return entry?.sdkSessionId;
|
|
73
|
+
}
|
|
74
|
+
/** Link a new SDK session to a channel key */
|
|
75
|
+
setSdkSessionId(key, sessionId) {
|
|
76
|
+
// Unlink any existing session for this key
|
|
77
|
+
this.clearSdkSessionId(key);
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
this.registry.push({
|
|
80
|
+
sdkSessionId: sessionId,
|
|
81
|
+
channelKey: key,
|
|
82
|
+
createdAt: now,
|
|
83
|
+
lastActiveAt: now,
|
|
84
|
+
unlinkedAt: null,
|
|
85
|
+
expiresAt: null,
|
|
86
|
+
});
|
|
87
|
+
this.saveRegistry();
|
|
88
|
+
}
|
|
89
|
+
/** Touch the active session (update lastActiveAt) */
|
|
90
|
+
touchSession(key) {
|
|
91
|
+
const entry = this.registry.find((e) => e.channelKey === key && e.unlinkedAt === null);
|
|
92
|
+
if (entry) {
|
|
93
|
+
entry.lastActiveAt = Date.now();
|
|
94
|
+
this.saveRegistry();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** List all SDK session entries */
|
|
98
|
+
listSdkSessionIds() {
|
|
99
|
+
return this.registry
|
|
100
|
+
.filter((e) => e.unlinkedAt === null)
|
|
101
|
+
.map((e) => [e.channelKey, e.sdkSessionId]);
|
|
102
|
+
}
|
|
103
|
+
/** List all sessions including unlinked */
|
|
104
|
+
listAllSessions() {
|
|
105
|
+
return [...this.registry];
|
|
106
|
+
}
|
|
107
|
+
/** Unlink a session (marks for deletion after TTL) */
|
|
108
|
+
clearSdkSessionId(key) {
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
for (const entry of this.registry) {
|
|
111
|
+
if (entry.channelKey === key && entry.unlinkedAt === null) {
|
|
112
|
+
entry.unlinkedAt = now;
|
|
113
|
+
entry.expiresAt = now + UNLINKED_TTL_MS;
|
|
114
|
+
log.info({ key, sessionId: entry.sdkSessionId, expiresAt: new Date(entry.expiresAt).toISOString() }, "Session unlinked, will be deleted in 30 days");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
this.saveRegistry();
|
|
118
|
+
}
|
|
119
|
+
/** Delete expired unlinked sessions and their SDK JSONL files */
|
|
120
|
+
cleanupExpired() {
|
|
121
|
+
const now = Date.now();
|
|
122
|
+
const sdkDir = getSdkSessionDir();
|
|
123
|
+
const expired = this.registry.filter((e) => e.expiresAt !== null && e.expiresAt <= now);
|
|
124
|
+
for (const entry of expired) {
|
|
125
|
+
// Delete the SDK JSONL file
|
|
126
|
+
const sdkFile = join(sdkDir, `${entry.sdkSessionId}.jsonl`);
|
|
127
|
+
if (existsSync(sdkFile)) {
|
|
128
|
+
try {
|
|
129
|
+
unlinkSync(sdkFile);
|
|
130
|
+
log.info({ sessionId: entry.sdkSessionId }, "Deleted expired SDK session file");
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
log.warn({ sessionId: entry.sdkSessionId }, "Failed to delete expired SDK session file");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (expired.length > 0) {
|
|
138
|
+
this.registry = this.registry.filter((e) => e.expiresAt === null || e.expiresAt > now);
|
|
139
|
+
this.saveRegistry();
|
|
140
|
+
log.info({ count: expired.length }, "Cleaned up expired sessions");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// --- Registry persistence ---
|
|
144
|
+
get registryPath() {
|
|
145
|
+
return join(this.dir, "_sessions.json");
|
|
146
|
+
}
|
|
147
|
+
loadRegistry() {
|
|
148
|
+
const file = this.registryPath;
|
|
149
|
+
if (!existsSync(file)) {
|
|
150
|
+
// Migrate from old _sdk_sessions.json if it exists
|
|
151
|
+
this.migrateOldFormat();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const data = JSON.parse(readFileSync(file, "utf-8"));
|
|
156
|
+
this.registry = data.sessions ?? [];
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
this.registry = [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
saveRegistry() {
|
|
163
|
+
const data = { version: 1, sessions: this.registry };
|
|
164
|
+
writeFileSync(this.registryPath, JSON.stringify(data, null, 2) + "\n");
|
|
165
|
+
}
|
|
166
|
+
/** Migrate from the old simple key→value format */
|
|
167
|
+
migrateOldFormat() {
|
|
168
|
+
const oldFile = join(this.dir, "_sdk_sessions.json");
|
|
169
|
+
if (!existsSync(oldFile))
|
|
170
|
+
return;
|
|
171
|
+
try {
|
|
172
|
+
const data = JSON.parse(readFileSync(oldFile, "utf-8"));
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
for (const [key, sessionId] of Object.entries(data)) {
|
|
175
|
+
this.registry.push({
|
|
176
|
+
sdkSessionId: sessionId,
|
|
177
|
+
channelKey: key,
|
|
178
|
+
createdAt: now,
|
|
179
|
+
lastActiveAt: now,
|
|
180
|
+
unlinkedAt: null,
|
|
181
|
+
expiresAt: null,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
this.saveRegistry();
|
|
185
|
+
unlinkSync(oldFile);
|
|
186
|
+
log.info({ count: this.registry.length }, "Migrated old session format");
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Ignore migration errors
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// --- Transcripts ---
|
|
193
|
+
transcriptPath(key) {
|
|
194
|
+
const safe = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
195
|
+
return join(this.dir, `${safe}.jsonl`);
|
|
196
|
+
}
|
|
197
|
+
loadTranscript(key) {
|
|
198
|
+
const file = this.transcriptPath(key);
|
|
199
|
+
if (!existsSync(file))
|
|
200
|
+
return [];
|
|
201
|
+
const lines = readFileSync(file, "utf-8").trim().split("\n");
|
|
202
|
+
const messages = [];
|
|
203
|
+
for (const line of lines) {
|
|
204
|
+
if (!line)
|
|
205
|
+
continue;
|
|
206
|
+
try {
|
|
207
|
+
messages.push(JSON.parse(line));
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Skip malformed lines
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return messages;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/sessions/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface SessionMessage {
|
|
2
|
+
role: "user" | "assistant";
|
|
3
|
+
content: string;
|
|
4
|
+
channel: string;
|
|
5
|
+
senderName?: string;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
}
|
|
8
|
+
export interface Session {
|
|
9
|
+
key: string;
|
|
10
|
+
messages: SessionMessage[];
|
|
11
|
+
createdAt: number;
|
|
12
|
+
updatedAt: number;
|
|
13
|
+
}
|
|
14
|
+
export interface SessionEntry {
|
|
15
|
+
/** SDK session ID (UUID) */
|
|
16
|
+
sdkSessionId: string;
|
|
17
|
+
/** Channel session key (e.g. "telegram:12345") */
|
|
18
|
+
channelKey: string;
|
|
19
|
+
/** When this session was created */
|
|
20
|
+
createdAt: number;
|
|
21
|
+
/** When this session was last used */
|
|
22
|
+
lastActiveAt: number;
|
|
23
|
+
/** If unlinked, when it was unlinked. Null if active. */
|
|
24
|
+
unlinkedAt: number | null;
|
|
25
|
+
/** When this session should be deleted (unlinkedAt + TTL). Null if active. */
|
|
26
|
+
expiresAt: number | null;
|
|
27
|
+
}
|
|
28
|
+
export interface SessionRegistry {
|
|
29
|
+
version: number;
|
|
30
|
+
sessions: SessionEntry[];
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sessions/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8EAA8E;IAC9E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,YAAY,EAAE,CAAC;CAC1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sessions/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class SkillSync {
|
|
2
|
+
private watcher;
|
|
3
|
+
private targetDir;
|
|
4
|
+
private sourceDir;
|
|
5
|
+
constructor(cwd?: string);
|
|
6
|
+
/** Initial sync + start watching for changes */
|
|
7
|
+
start(): void;
|
|
8
|
+
stop(): void;
|
|
9
|
+
/** Copy workspace skills to ~/.claude/skills/ with tomo- prefix */
|
|
10
|
+
private sync;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/skills/sync.ts"],"names":[],"mappings":"AAkBA,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;gBAEd,GAAG,GAAE,MAAsB;IAKvC,gDAAgD;IAChD,KAAK,IAAI,IAAI;IAeb,IAAI,IAAI,IAAI;IAKZ,mEAAmE;IACnE,OAAO,CAAC,IAAI;CA+Bb"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { watch } from "node:fs";
|
|
2
|
+
import { cpSync, mkdirSync, existsSync, readdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { log } from "../logger.js";
|
|
5
|
+
/** Source: ~/.tomo/workspace/skills/ (user's skills, copied by tomo init) */
|
|
6
|
+
function getSourceDir(cwd) {
|
|
7
|
+
return join(cwd, "skills");
|
|
8
|
+
}
|
|
9
|
+
/** Target: {cwd}/.claude/skills/ (project-level, discovered by SDK via settingSources) */
|
|
10
|
+
function getTargetDir(cwd) {
|
|
11
|
+
return join(cwd, ".claude", "skills");
|
|
12
|
+
}
|
|
13
|
+
const SKILL_PREFIX = "tomo-";
|
|
14
|
+
export class SkillSync {
|
|
15
|
+
watcher = null;
|
|
16
|
+
targetDir;
|
|
17
|
+
sourceDir;
|
|
18
|
+
constructor(cwd = process.cwd()) {
|
|
19
|
+
this.sourceDir = getSourceDir(cwd);
|
|
20
|
+
this.targetDir = getTargetDir(cwd);
|
|
21
|
+
}
|
|
22
|
+
/** Initial sync + start watching for changes */
|
|
23
|
+
start() {
|
|
24
|
+
this.sync();
|
|
25
|
+
if (!existsSync(this.sourceDir))
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
this.watcher = watch(this.sourceDir, { recursive: true }, (_event, _filename) => {
|
|
29
|
+
this.sync();
|
|
30
|
+
});
|
|
31
|
+
log.info({ source: this.sourceDir, target: this.targetDir }, "Skill sync watcher started");
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
log.warn("Could not start skill file watcher, skills will only sync on startup");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
stop() {
|
|
38
|
+
this.watcher?.close();
|
|
39
|
+
this.watcher = null;
|
|
40
|
+
}
|
|
41
|
+
/** Copy workspace skills to ~/.claude/skills/ with tomo- prefix */
|
|
42
|
+
sync() {
|
|
43
|
+
if (!existsSync(this.sourceDir))
|
|
44
|
+
return;
|
|
45
|
+
mkdirSync(this.targetDir, { recursive: true });
|
|
46
|
+
const workspaceSkills = new Set();
|
|
47
|
+
for (const entry of readdirSync(this.sourceDir, { withFileTypes: true })) {
|
|
48
|
+
if (!entry.isDirectory())
|
|
49
|
+
continue;
|
|
50
|
+
const prefixed = `${SKILL_PREFIX}${entry.name}`;
|
|
51
|
+
workspaceSkills.add(prefixed);
|
|
52
|
+
const src = join(this.sourceDir, entry.name);
|
|
53
|
+
const dst = join(this.targetDir, prefixed);
|
|
54
|
+
cpSync(src, dst, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
// Remove tomo- prefixed skills that are no longer in workspace
|
|
57
|
+
if (existsSync(this.targetDir)) {
|
|
58
|
+
for (const entry of readdirSync(this.targetDir, { withFileTypes: true })) {
|
|
59
|
+
if (!entry.isDirectory())
|
|
60
|
+
continue;
|
|
61
|
+
if (!entry.name.startsWith(SKILL_PREFIX))
|
|
62
|
+
continue;
|
|
63
|
+
if (!workspaceSkills.has(entry.name)) {
|
|
64
|
+
rmSync(join(this.targetDir, entry.name), { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
log.debug({ skills: [...workspaceSkills] }, "Skills synced");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/skills/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAoGA,wBAAgB,iBAAiB,IAAI,MAAM,CAG1C"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { readFileSync, existsSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join, dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const TOMO_HOME = join(homedir(), ".tomo");
|
|
7
|
+
const WORKSPACE_DIR = process.env.TOMO_WORKSPACE ?? join(TOMO_HOME, "workspace");
|
|
8
|
+
const DEFAULTS_DIR = resolve(__dirname, "../../defaults");
|
|
9
|
+
const MEMORY_DIR = join(WORKSPACE_DIR, "memory");
|
|
10
|
+
const MEMORY_ENTRYPOINT = join(MEMORY_DIR, "MEMORY.md");
|
|
11
|
+
const MAX_MEMORY_LINES = 200;
|
|
12
|
+
/** Load a .md file from workspace, falling back to bundled defaults */
|
|
13
|
+
function load(name) {
|
|
14
|
+
const userPath = join(WORKSPACE_DIR, `${name}.md`);
|
|
15
|
+
if (existsSync(userPath)) {
|
|
16
|
+
return readFileSync(userPath, "utf-8").trim();
|
|
17
|
+
}
|
|
18
|
+
const defaultPath = join(DEFAULTS_DIR, `${name}.md`);
|
|
19
|
+
if (existsSync(defaultPath)) {
|
|
20
|
+
return readFileSync(defaultPath, "utf-8").trim();
|
|
21
|
+
}
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
function loadMemory() {
|
|
25
|
+
mkdirSync(MEMORY_DIR, { recursive: true });
|
|
26
|
+
const instructions = `
|
|
27
|
+
# MEMORY — Your Persistent Memory
|
|
28
|
+
|
|
29
|
+
You have a file-based memory system at ${MEMORY_DIR}/. This directory is yours — read from it and write to it freely.
|
|
30
|
+
|
|
31
|
+
## How it works
|
|
32
|
+
|
|
33
|
+
- **MEMORY.md** is your index file. It's loaded into your context every conversation.
|
|
34
|
+
- Each memory is a separate .md file with YAML frontmatter (name, description, type).
|
|
35
|
+
- MEMORY.md contains one-line pointers to memory files: \`- [Title](file.md) — short description\`
|
|
36
|
+
|
|
37
|
+
## Memory types
|
|
38
|
+
|
|
39
|
+
| Type | Purpose |
|
|
40
|
+
|------|---------|
|
|
41
|
+
| **user** | Who the user is — role, preferences, knowledge |
|
|
42
|
+
| **feedback** | How to approach work — corrections and confirmed approaches |
|
|
43
|
+
| **project** | Ongoing work, goals, deadlines |
|
|
44
|
+
| **reference** | Pointers to external resources |
|
|
45
|
+
|
|
46
|
+
## How to save
|
|
47
|
+
|
|
48
|
+
1. Write the memory file:
|
|
49
|
+
\`\`\`markdown
|
|
50
|
+
---
|
|
51
|
+
name: memory-name
|
|
52
|
+
description: one-line description
|
|
53
|
+
type: user
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
Memory content here.
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
2. Add a pointer to MEMORY.md: \`- [Title](file.md) — one-line hook\`
|
|
60
|
+
|
|
61
|
+
## When to save
|
|
62
|
+
|
|
63
|
+
- When the user explicitly asks you to remember something
|
|
64
|
+
- When you learn something about the user that would help future conversations
|
|
65
|
+
- When the user corrects your approach or confirms a non-obvious choice
|
|
66
|
+
|
|
67
|
+
## When NOT to save
|
|
68
|
+
|
|
69
|
+
- Code patterns or architecture (derive from current state)
|
|
70
|
+
- Ephemeral task details
|
|
71
|
+
- Anything already in SOUL.md, AGENT.md, or IDENTITY.md
|
|
72
|
+
|
|
73
|
+
## Rules
|
|
74
|
+
|
|
75
|
+
- Keep MEMORY.md under ${MAX_MEMORY_LINES} lines
|
|
76
|
+
- Update or remove stale memories rather than accumulating
|
|
77
|
+
- Check if a memory already exists before creating a duplicate
|
|
78
|
+
- Convert relative dates to absolute dates when saving`.trim();
|
|
79
|
+
let memoryContent;
|
|
80
|
+
if (existsSync(MEMORY_ENTRYPOINT)) {
|
|
81
|
+
const raw = readFileSync(MEMORY_ENTRYPOINT, "utf-8").trim();
|
|
82
|
+
const lines = raw.split("\n");
|
|
83
|
+
if (lines.length > MAX_MEMORY_LINES) {
|
|
84
|
+
memoryContent = lines.slice(0, MAX_MEMORY_LINES).join("\n") + `\n\n(truncated — ${lines.length - MAX_MEMORY_LINES} lines omitted)`;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
memoryContent = raw;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
memoryContent = "(currently empty)";
|
|
92
|
+
}
|
|
93
|
+
return `${instructions}\n\n## Current MEMORY.md\n\n${memoryContent}`;
|
|
94
|
+
}
|
|
95
|
+
export function buildSystemPrompt() {
|
|
96
|
+
const sections = [load("SOUL"), load("AGENT"), load("IDENTITY"), loadMemory()].filter(Boolean);
|
|
97
|
+
return sections.join("\n\n---\n\n");
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tomo-ai",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Personal assistant powered by Claude Agent SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"tomo": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"defaults"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsx watch src/cli.ts -- start",
|
|
17
|
+
"start": "node dist/cli.js start",
|
|
18
|
+
"tomo": "tsx src/cli.ts",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"ai",
|
|
23
|
+
"assistant",
|
|
24
|
+
"claude",
|
|
25
|
+
"telegram",
|
|
26
|
+
"chatbot",
|
|
27
|
+
"agent"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.0.0"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.92",
|
|
35
|
+
"@clack/prompts": "^1.2.0",
|
|
36
|
+
"commander": "^13.1.0",
|
|
37
|
+
"croner": "^9.0.0",
|
|
38
|
+
"grammy": "^1.35.0",
|
|
39
|
+
"pino": "^9.6.0",
|
|
40
|
+
"pino-pretty": "^13.0.0",
|
|
41
|
+
"zod": "^4.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"dotenv": "^16.4.7",
|
|
46
|
+
"tsx": "^4.19.0",
|
|
47
|
+
"typescript": "^5.7.0"
|
|
48
|
+
}
|
|
49
|
+
}
|