@mr-jones123/toji 0.1.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 +158 -0
- package/package.json +47 -0
- package/packages/toji-comms/README.md +71 -0
- package/packages/toji-comms/src/cli/agents.ts +121 -0
- package/packages/toji-comms/src/cli/mmx.ts +65 -0
- package/packages/toji-comms/src/cli/subprocess.ts +47 -0
- package/packages/toji-comms/src/comms/orchestrator.ts +92 -0
- package/packages/toji-comms/src/comms/prompt.ts +84 -0
- package/packages/toji-comms/src/comms/store.ts +145 -0
- package/packages/toji-comms/src/comms/types.ts +94 -0
- package/packages/toji-comms/src/db/connection.ts +58 -0
- package/packages/toji-comms/src/db/migrations.ts +69 -0
- package/packages/toji-comms/src/index.ts +368 -0
- package/packages/toji-comms/src/mcp/client.ts +71 -0
- package/packages/toji-comms/src/mcp/server.ts +81 -0
- package/packages/toji-mem/README.md +52 -0
- package/packages/toji-mem/grammars/manifest.json +9 -0
- package/packages/toji-mem/grammars/tree-sitter-cpp.wasm +0 -0
- package/packages/toji-mem/grammars/tree-sitter-dart.wasm +0 -0
- package/packages/toji-mem/grammars/tree-sitter-java.wasm +0 -0
- package/packages/toji-mem/grammars/tree-sitter-javascript.wasm +0 -0
- package/packages/toji-mem/grammars/tree-sitter-python.wasm +0 -0
- package/packages/toji-mem/grammars/tree-sitter-tsx.wasm +0 -0
- package/packages/toji-mem/grammars/tree-sitter-typescript.wasm +0 -0
- package/packages/toji-mem/src/db/connection.ts +58 -0
- package/packages/toji-mem/src/db/migrations.ts +181 -0
- package/packages/toji-mem/src/index.ts +326 -0
- package/packages/toji-mem/src/indexer/file-walker.ts +45 -0
- package/packages/toji-mem/src/indexer/index-project.ts +277 -0
- package/packages/toji-mem/src/indexer/parsers/cpp.ts +81 -0
- package/packages/toji-mem/src/indexer/parsers/dart.ts +91 -0
- package/packages/toji-mem/src/indexer/parsers/java.ts +83 -0
- package/packages/toji-mem/src/indexer/parsers/python.ts +84 -0
- package/packages/toji-mem/src/indexer/parsers/registry.ts +28 -0
- package/packages/toji-mem/src/indexer/parsers/tree-sitter-loader.ts +39 -0
- package/packages/toji-mem/src/indexer/parsers/types.ts +48 -0
- package/packages/toji-mem/src/indexer/parsers/typescript.ts +105 -0
- package/packages/toji-mem/src/standards/store.ts +52 -0
- package/packages/toji-mem/src/tools/blast-radius.ts +98 -0
- package/packages/toji-mem/src/tools/graph-explore.ts +186 -0
- package/packages/toji-mem/src/tools/project-overview.ts +102 -0
- package/packages/toji-mem/src/tools/query-memory.ts +105 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { TojiCommsDatabase } from "../db/connection";
|
|
2
|
+
import type { CliAgentResult, CommsContextType, CommsRequest, CommsThread, CommsThreadContext, CommsTurn } from "./types";
|
|
3
|
+
|
|
4
|
+
export function getOrCreateThread(database: TojiCommsDatabase, request: CommsRequest): CommsThread {
|
|
5
|
+
if (request.threadId !== undefined) {
|
|
6
|
+
const thread = database.query<CommsThread, [number]>(`SELECT * FROM comms_threads WHERE id = ?`).get(request.threadId);
|
|
7
|
+
if (!thread) throw new Error(`No Toji Comms thread found for id ${request.threadId}.`);
|
|
8
|
+
return thread;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const active = database
|
|
12
|
+
.query<CommsThread, [string]>(`SELECT * FROM comms_threads WHERE cwd = ? AND status = 'active' ORDER BY updated_at DESC, id DESC LIMIT 1`)
|
|
13
|
+
.get(request.cwd);
|
|
14
|
+
if (active) return active;
|
|
15
|
+
|
|
16
|
+
database
|
|
17
|
+
.query(
|
|
18
|
+
`INSERT INTO comms_threads (title, cwd, session_id, mode, status)
|
|
19
|
+
VALUES (?, ?, ?, ?, 'active')`,
|
|
20
|
+
)
|
|
21
|
+
.run(request.title ?? titleFromRequest(request), request.cwd, request.sessionId ?? null, request.mode ?? "discuss");
|
|
22
|
+
|
|
23
|
+
return getThread(database, lastInsertId(database));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getThread(database: TojiCommsDatabase, threadId: number): CommsThread {
|
|
27
|
+
const thread = database.query<CommsThread, [number]>(`SELECT * FROM comms_threads WHERE id = ?`).get(threadId);
|
|
28
|
+
if (!thread) throw new Error(`No Toji Comms thread found for id ${threadId}.`);
|
|
29
|
+
return thread;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function addTurn(
|
|
33
|
+
database: TojiCommsDatabase,
|
|
34
|
+
turn: {
|
|
35
|
+
threadId: number;
|
|
36
|
+
role: CommsTurn["role"];
|
|
37
|
+
sourceAgent?: string;
|
|
38
|
+
provider?: string;
|
|
39
|
+
model?: string;
|
|
40
|
+
content: string;
|
|
41
|
+
contextJson?: unknown;
|
|
42
|
+
},
|
|
43
|
+
): number {
|
|
44
|
+
database
|
|
45
|
+
.query(
|
|
46
|
+
`INSERT INTO comms_turns (thread_id, role, source_agent, provider, model, content, context_json)
|
|
47
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
48
|
+
)
|
|
49
|
+
.run(
|
|
50
|
+
turn.threadId,
|
|
51
|
+
turn.role,
|
|
52
|
+
turn.sourceAgent ?? null,
|
|
53
|
+
turn.provider ?? null,
|
|
54
|
+
turn.model ?? null,
|
|
55
|
+
turn.content,
|
|
56
|
+
turn.contextJson ? JSON.stringify(turn.contextJson) : null,
|
|
57
|
+
);
|
|
58
|
+
database.query(`UPDATE comms_threads SET updated_at = CURRENT_TIMESTAMP WHERE id = ?`).run(turn.threadId);
|
|
59
|
+
return lastInsertId(database);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getRecentTurns(database: TojiCommsDatabase, threadId: number, limit = 10): CommsTurn[] {
|
|
63
|
+
return database
|
|
64
|
+
.query<CommsTurn, [number, number]>(`SELECT * FROM comms_turns WHERE thread_id = ? ORDER BY id DESC LIMIT ?`)
|
|
65
|
+
.all(threadId, limit)
|
|
66
|
+
.reverse();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function addThreadContext(
|
|
70
|
+
database: TojiCommsDatabase,
|
|
71
|
+
input: { threadId: number; type: CommsContextType; title: string; content: string; source?: string },
|
|
72
|
+
): number {
|
|
73
|
+
database
|
|
74
|
+
.query(
|
|
75
|
+
`INSERT INTO comms_context (thread_id, type, title, content, source)
|
|
76
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
77
|
+
)
|
|
78
|
+
.run(input.threadId, input.type, input.title, input.content, input.source ?? null);
|
|
79
|
+
return lastInsertId(database);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function listThreadContext(database: TojiCommsDatabase, threadId: number, limit = 10): CommsThreadContext[] {
|
|
83
|
+
return database
|
|
84
|
+
.query<CommsThreadContext, [number, number]>(`SELECT * FROM comms_context WHERE thread_id = ? ORDER BY id DESC LIMIT ?`)
|
|
85
|
+
.all(threadId, limit)
|
|
86
|
+
.reverse();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function listThreadContextByIds(database: TojiCommsDatabase, threadId: number, ids: number[]): CommsThreadContext[] {
|
|
90
|
+
if (ids.length === 0) return [];
|
|
91
|
+
return database
|
|
92
|
+
.query<CommsThreadContext, Array<number>>(
|
|
93
|
+
`SELECT * FROM comms_context
|
|
94
|
+
WHERE thread_id = ? AND id IN (${ids.map(() => "?").join(", ")})
|
|
95
|
+
ORDER BY id`,
|
|
96
|
+
)
|
|
97
|
+
.all(threadId, ...ids);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function deleteThreadContext(database: TojiCommsDatabase, id: number): boolean {
|
|
101
|
+
const result = database.query<{ changes: number }, [number]>(`DELETE FROM comms_context WHERE id = ?`).run(id) as { changes?: number };
|
|
102
|
+
return (result.changes ?? 0) > 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function clearThreadContext(database: TojiCommsDatabase, threadId: number): void {
|
|
106
|
+
database.query(`DELETE FROM comms_context WHERE thread_id = ?`).run(threadId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function storeRun(
|
|
110
|
+
database: TojiCommsDatabase,
|
|
111
|
+
threadId: number,
|
|
112
|
+
turnId: number | undefined,
|
|
113
|
+
run: CliAgentResult,
|
|
114
|
+
targetModel?: string,
|
|
115
|
+
renderedPrompt = "",
|
|
116
|
+
): void {
|
|
117
|
+
database
|
|
118
|
+
.query(
|
|
119
|
+
`INSERT INTO comms_runs (thread_id, turn_id, target_provider, target_model, command_json, rendered_prompt, exit_code, stdout, stderr, duration_ms)
|
|
120
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
121
|
+
)
|
|
122
|
+
.run(
|
|
123
|
+
threadId,
|
|
124
|
+
turnId ?? null,
|
|
125
|
+
run.agent,
|
|
126
|
+
targetModel ?? null,
|
|
127
|
+
JSON.stringify(run.command),
|
|
128
|
+
renderedPrompt,
|
|
129
|
+
run.exitCode,
|
|
130
|
+
run.stdout,
|
|
131
|
+
run.stderr,
|
|
132
|
+
run.durationMs,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function titleFromRequest(request: CommsRequest): string {
|
|
137
|
+
const source = request.title ?? request.context.userRequest ?? request.context.sourceMessage;
|
|
138
|
+
return source.length > 80 ? `${source.slice(0, 77)}...` : source;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function lastInsertId(database: TojiCommsDatabase): number {
|
|
142
|
+
const row = database.query<{ id: number }>(`SELECT last_insert_rowid() AS id`).get();
|
|
143
|
+
if (!row) throw new Error("Unable to read SQLite last_insert_rowid().");
|
|
144
|
+
return row.id;
|
|
145
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export interface SourceModelInfo {
|
|
2
|
+
provider?: string;
|
|
3
|
+
id?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type CommsMode = "discuss" | "ask" | "critique" | "decide" | "verify" | "plan";
|
|
7
|
+
export type ContextMode = "brief" | "thread" | "recentTurns" | "threadContext";
|
|
8
|
+
export type CommsContextType = "project_overview" | "graph_result" | "file_snippet" | "decision" | "constraint" | "research_summary" | "benchmark_result" | "user_preference" | "note";
|
|
9
|
+
|
|
10
|
+
export interface CommsContextPacket {
|
|
11
|
+
userRequest?: string;
|
|
12
|
+
sourceMessage: string;
|
|
13
|
+
knownContext?: string[];
|
|
14
|
+
questionsForPeer?: string[];
|
|
15
|
+
contextMode?: ContextMode[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type TargetProvider = "mmx" | "claude" | "codex";
|
|
19
|
+
|
|
20
|
+
export interface CommsRequest {
|
|
21
|
+
cwd: string;
|
|
22
|
+
threadId?: number;
|
|
23
|
+
title?: string;
|
|
24
|
+
mode?: CommsMode;
|
|
25
|
+
sourceModel?: SourceModelInfo;
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
targetProvider?: TargetProvider;
|
|
28
|
+
targetModel?: string;
|
|
29
|
+
system?: string;
|
|
30
|
+
includeThreadContext?: boolean;
|
|
31
|
+
contextIds?: number[];
|
|
32
|
+
context: CommsContextPacket;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CommsThread {
|
|
36
|
+
id: number;
|
|
37
|
+
title: string;
|
|
38
|
+
cwd: string;
|
|
39
|
+
session_id?: string | null;
|
|
40
|
+
mode: CommsMode;
|
|
41
|
+
status: string;
|
|
42
|
+
summary?: string | null;
|
|
43
|
+
created_at: string;
|
|
44
|
+
updated_at: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface CommsThreadContext {
|
|
48
|
+
id: number;
|
|
49
|
+
thread_id: number;
|
|
50
|
+
type: CommsContextType;
|
|
51
|
+
title: string;
|
|
52
|
+
content: string;
|
|
53
|
+
source?: string | null;
|
|
54
|
+
created_at: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CommsTurn {
|
|
58
|
+
id: number;
|
|
59
|
+
thread_id: number;
|
|
60
|
+
role: "user" | "source_ai" | "peer_ai" | "system";
|
|
61
|
+
source_agent?: string | null;
|
|
62
|
+
provider?: string | null;
|
|
63
|
+
model?: string | null;
|
|
64
|
+
content: string;
|
|
65
|
+
context_json?: string | null;
|
|
66
|
+
created_at: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CliAgentInput {
|
|
70
|
+
cwd: string;
|
|
71
|
+
prompt: string;
|
|
72
|
+
system?: string;
|
|
73
|
+
model?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface CliAgentResult {
|
|
77
|
+
agent: TargetProvider;
|
|
78
|
+
command: string[];
|
|
79
|
+
exitCode: number | null;
|
|
80
|
+
stdout: string;
|
|
81
|
+
stderr: string;
|
|
82
|
+
durationMs: number;
|
|
83
|
+
reply: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface CommsResult {
|
|
87
|
+
threadId: number;
|
|
88
|
+
sourceTurnId: number;
|
|
89
|
+
peerTurnId?: number;
|
|
90
|
+
status: "completed" | "failed";
|
|
91
|
+
prompt: string;
|
|
92
|
+
reply: string;
|
|
93
|
+
run: CliAgentResult;
|
|
94
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { DatabaseSync, type SQLInputValue, type StatementSync } from "node:sqlite";
|
|
5
|
+
|
|
6
|
+
import { runMigrations } from "./migrations";
|
|
7
|
+
|
|
8
|
+
export interface TojiCommsStatement<Result = unknown, Params extends SQLInputValue[] = SQLInputValue[]> {
|
|
9
|
+
run(...params: Params): unknown;
|
|
10
|
+
get(...params: Params): Result | undefined;
|
|
11
|
+
all(...params: Params): Result[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface TojiCommsDatabase {
|
|
15
|
+
exec(sql: string): void;
|
|
16
|
+
query<Result = unknown, Params extends SQLInputValue[] = SQLInputValue[]>(sql: string): TojiCommsStatement<Result, Params>;
|
|
17
|
+
close(): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class NodeSqliteDatabase implements TojiCommsDatabase {
|
|
21
|
+
readonly #database: DatabaseSync;
|
|
22
|
+
|
|
23
|
+
constructor(databasePath: string) {
|
|
24
|
+
this.#database = new DatabaseSync(databasePath, { open: true });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
exec(sql: string): void {
|
|
28
|
+
this.#database.exec(sql);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
query<Result = unknown, Params extends SQLInputValue[] = SQLInputValue[]>(sql: string): TojiCommsStatement<Result, Params> {
|
|
32
|
+
const statement = this.#database.prepare(sql) as StatementSync;
|
|
33
|
+
return {
|
|
34
|
+
run: (...params: Params) => statement.run(...params),
|
|
35
|
+
get: (...params: Params) => statement.get(...params) as Result | undefined,
|
|
36
|
+
all: (...params: Params) => statement.all(...params) as Result[],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
close(): void {
|
|
41
|
+
this.#database.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getDefaultDatabasePath(): string {
|
|
46
|
+
return join(homedir(), ".pi", "agent", "toji-comms", "toji-comms.sqlite");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function openTojiCommsDatabase(databasePath = getDefaultDatabasePath()): TojiCommsDatabase {
|
|
50
|
+
mkdirSync(dirname(databasePath), { recursive: true });
|
|
51
|
+
|
|
52
|
+
const database = new NodeSqliteDatabase(databasePath);
|
|
53
|
+
database.exec("PRAGMA foreign_keys = ON;");
|
|
54
|
+
database.exec("PRAGMA journal_mode = WAL;");
|
|
55
|
+
runMigrations(database);
|
|
56
|
+
|
|
57
|
+
return database;
|
|
58
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { TojiCommsDatabase } from "./connection";
|
|
2
|
+
|
|
3
|
+
const schema = `
|
|
4
|
+
CREATE TABLE IF NOT EXISTS comms_threads (
|
|
5
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6
|
+
title TEXT NOT NULL,
|
|
7
|
+
cwd TEXT NOT NULL,
|
|
8
|
+
session_id TEXT,
|
|
9
|
+
mode TEXT NOT NULL DEFAULT 'discuss',
|
|
10
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
11
|
+
summary TEXT,
|
|
12
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
13
|
+
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE TABLE IF NOT EXISTS comms_turns (
|
|
17
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
18
|
+
thread_id INTEGER NOT NULL REFERENCES comms_threads(id) ON DELETE CASCADE,
|
|
19
|
+
role TEXT NOT NULL,
|
|
20
|
+
source_agent TEXT,
|
|
21
|
+
provider TEXT,
|
|
22
|
+
model TEXT,
|
|
23
|
+
content TEXT NOT NULL,
|
|
24
|
+
context_json TEXT,
|
|
25
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS comms_runs (
|
|
29
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
30
|
+
thread_id INTEGER NOT NULL REFERENCES comms_threads(id) ON DELETE CASCADE,
|
|
31
|
+
turn_id INTEGER REFERENCES comms_turns(id) ON DELETE SET NULL,
|
|
32
|
+
target_provider TEXT NOT NULL,
|
|
33
|
+
target_model TEXT,
|
|
34
|
+
command_json TEXT NOT NULL,
|
|
35
|
+
rendered_prompt TEXT NOT NULL DEFAULT '',
|
|
36
|
+
exit_code INTEGER,
|
|
37
|
+
stdout TEXT NOT NULL DEFAULT '',
|
|
38
|
+
stderr TEXT NOT NULL DEFAULT '',
|
|
39
|
+
duration_ms INTEGER NOT NULL,
|
|
40
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE TABLE IF NOT EXISTS comms_context (
|
|
44
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
45
|
+
thread_id INTEGER NOT NULL REFERENCES comms_threads(id) ON DELETE CASCADE,
|
|
46
|
+
type TEXT NOT NULL,
|
|
47
|
+
title TEXT NOT NULL,
|
|
48
|
+
content TEXT NOT NULL,
|
|
49
|
+
source TEXT,
|
|
50
|
+
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_comms_threads_updated ON comms_threads(updated_at);
|
|
54
|
+
CREATE INDEX IF NOT EXISTS idx_comms_threads_cwd ON comms_threads(cwd, status, updated_at);
|
|
55
|
+
CREATE INDEX IF NOT EXISTS idx_comms_turns_thread ON comms_turns(thread_id, id);
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_comms_runs_thread ON comms_runs(thread_id, created_at);
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_comms_context_thread ON comms_context(thread_id, created_at);
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
export function runMigrations(database: TojiCommsDatabase): void {
|
|
61
|
+
database.exec(schema);
|
|
62
|
+
addColumnIfMissing(database, "comms_runs", "rendered_prompt", "TEXT NOT NULL DEFAULT ''");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function addColumnIfMissing(database: TojiCommsDatabase, table: string, column: string, definition: string): void {
|
|
66
|
+
const columns = database.query<{ name: string }>(`PRAGMA table_info(${table})`).all();
|
|
67
|
+
if (columns.some((item) => item.name === column)) return;
|
|
68
|
+
database.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition};`);
|
|
69
|
+
}
|