@mr-jones123/toji 0.1.1 → 0.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mr-jones123/toji",
3
- "version": "0.1.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "description": "Toji Pi extensions for code memory and AI-to-AI comms.",
6
6
  "keywords": [
@@ -3,6 +3,7 @@ import type { TojiDatabase } from "./connection";
3
3
  const tables = `
4
4
  CREATE TABLE IF NOT EXISTS projects (
5
5
  id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ project_key TEXT UNIQUE,
6
7
  root_path TEXT NOT NULL UNIQUE,
7
8
  name TEXT NOT NULL,
8
9
  indexed_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
@@ -113,6 +114,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS standards_fts USING fts5(
113
114
  `;
114
115
 
115
116
  const indexes = `
117
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_projects_project_key ON projects(project_key);
116
118
  CREATE INDEX IF NOT EXISTS idx_files_project_path ON files(project_id, path);
117
119
  CREATE INDEX IF NOT EXISTS idx_files_project_name ON files(project_id, name);
118
120
  CREATE INDEX IF NOT EXISTS idx_files_lower_path ON files(lower(path));
@@ -141,6 +143,7 @@ export function runMigrations(database: TojiDatabase): void {
141
143
 
142
144
  function migrateExisting(database: TojiDatabase): void {
143
145
  for (const sql of [
146
+ "ALTER TABLE projects ADD COLUMN project_key TEXT",
144
147
  "ALTER TABLE files ADD COLUMN name TEXT NOT NULL DEFAULT ''",
145
148
  "ALTER TABLE symbols ADD COLUMN canon_name TEXT NOT NULL DEFAULT ''",
146
149
  "ALTER TABLE symbols ADD COLUMN docstring TEXT NOT NULL DEFAULT ''",
@@ -117,7 +117,7 @@ export default function (pi: ExtensionAPI) {
117
117
  description: "Return an overview of an indexed project: files by directory, languages, symbols, tests, and graph edge counts. Use this when asked to read or summarize an indexed codebase.",
118
118
  parameters: Type.Object({
119
119
  projectPath: Type.Optional(Type.String({ description: "Project root path. Defaults to current pi cwd." })),
120
- projectId: Type.Optional(Type.Number({ description: "Indexed project id." })),
120
+ projectId: Type.Optional(Type.Union([Type.Number(), Type.String()], { description: "Indexed project id or hex project key." })),
121
121
  includeSymbols: Type.Optional(Type.Boolean({ description: "Include symbols grouped by file. Defaults to true." })),
122
122
  }),
123
123
  async execute(_toolCallId, params, _signal, onUpdate, ctx) {
@@ -7,7 +7,7 @@ import { walkProjectFiles } from "./file-walker";
7
7
  import { getParserForPath, getSupportedExtensions } from "./parsers/registry";
8
8
 
9
9
  export interface IndexProjectResult {
10
- projectId: number;
10
+ projectId: string;
11
11
  rootPath: string;
12
12
  indexedFiles: number;
13
13
  symbols: number;
@@ -17,14 +17,15 @@ export interface IndexProjectResult {
17
17
 
18
18
  export async function indexProject(database: TojiDatabase, rootPath: string): Promise<IndexProjectResult> {
19
19
  const normalizedRoot = resolve(rootPath);
20
+ const projectKey = createHash("sha256").update(normalizedRoot).digest("hex").slice(0, 8);
20
21
  const project = database
21
- .query<{ id: number }, [string, string]>(
22
- `INSERT INTO projects (root_path, name, indexed_at)
23
- VALUES (?, ?, CURRENT_TIMESTAMP)
24
- ON CONFLICT(root_path) DO UPDATE SET indexed_at = CURRENT_TIMESTAMP
22
+ .query<{ id: number }, [string, string, string]>(
23
+ `INSERT INTO projects (project_key, root_path, name, indexed_at)
24
+ VALUES (?, ?, ?, CURRENT_TIMESTAMP)
25
+ ON CONFLICT(root_path) DO UPDATE SET project_key = excluded.project_key, indexed_at = CURRENT_TIMESTAMP
25
26
  RETURNING id`,
26
27
  )
27
- .get(normalizedRoot, basename(normalizedRoot));
28
+ .get(projectKey, normalizedRoot, basename(normalizedRoot));
28
29
 
29
30
  if (!project) throw new Error("Failed to create or update project");
30
31
 
@@ -47,7 +48,7 @@ export async function indexProject(database: TojiDatabase, rootPath: string): Pr
47
48
  )
48
49
  .get(project.id, project.id, project.id);
49
50
  return {
50
- projectId: project.id,
51
+ projectId: projectKey,
51
52
  rootPath: normalizedRoot,
52
53
  indexedFiles: files.length,
53
54
  symbols: counts?.symbols ?? 0,
@@ -168,7 +169,7 @@ export async function indexProject(database: TojiDatabase, rootPath: string): Pr
168
169
 
169
170
  createGraphEdges(database, project.id, files, fileIds, symbolsByFile, importsByFile, callsByFile, relationsByFile);
170
171
 
171
- return { projectId: project.id, rootPath: normalizedRoot, indexedFiles: files.length, symbols: symbolCount, imports: importCount, calls: callCount };
172
+ return { projectId: projectKey, rootPath: normalizedRoot, indexedFiles: files.length, symbols: symbolCount, imports: importCount, calls: callCount };
172
173
  }
173
174
 
174
175
  async function hashFiles(rootPath: string, files: string[]): Promise<Map<string, string>> {
@@ -271,7 +272,7 @@ function resolveImportPath(fromPath: string, source: string, files: string[]): s
271
272
  if (!source.startsWith(".")) return undefined;
272
273
 
273
274
  const basePath = normalize(join(dirname(fromPath), source));
274
- const candidates = [basePath, `${basePath}.ts`, `${basePath}.tsx`, `${basePath}.js`, `${basePath}.jsx`, `${basePath}.py`, join(basePath, "index.ts")];
275
+ const candidates = [basePath, `${basePath}.ts`, `${basePath}.tsx`, `${basePath}.js`, `${basePath}.jsx`, `${basePath}.py`, `${basePath}.dart`, join(basePath, "index.ts")];
275
276
  return candidates.find((candidate) => files.includes(candidate));
276
277
  }
277
278
 
@@ -4,7 +4,7 @@ import type { TojiDatabase } from "../db/connection";
4
4
 
5
5
  export interface ProjectOverviewOptions {
6
6
  projectPath?: string;
7
- projectId?: number;
7
+ projectId?: number | string;
8
8
  includeSymbols?: boolean;
9
9
  }
10
10
 
@@ -26,20 +26,36 @@ export function getProjectOverview(database: TojiDatabase, options: ProjectOverv
26
26
  };
27
27
 
28
28
  const overview: Record<string, unknown> = {
29
- project,
29
+ project: {
30
+ projectId: project.project_key ?? String(project.id),
31
+ root_path: project.root_path,
32
+ name: project.name,
33
+ indexed_at: project.indexed_at,
34
+ },
30
35
  counts,
31
36
  languages: database
32
37
  .query(`SELECT language, count(*) AS files FROM files WHERE project_id = ? GROUP BY language ORDER BY files DESC`)
33
38
  .all(project.id),
34
39
  directories: database
35
40
  .query(
36
- `SELECT CASE WHEN instr(path, '/') = 0 THEN '(root)' ELSE substr(path, 1, instr(path, '/') - 1) END AS directory,
37
- count(*) AS files
38
- FROM files
39
- WHERE project_id = ?
41
+ `WITH RECURSIVE split(path, directory, rest) AS (
42
+ SELECT path,
43
+ CASE WHEN instr(path, '/') = 0 THEN '(root)' ELSE substr(path, 1, instr(path, '/') - 1) END,
44
+ CASE WHEN instr(path, '/') = 0 THEN '' ELSE substr(path, instr(path, '/') + 1) END
45
+ FROM files
46
+ WHERE project_id = ?
47
+ UNION ALL
48
+ SELECT path,
49
+ directory || '/' || substr(rest, 1, instr(rest, '/') - 1),
50
+ substr(rest, instr(rest, '/') + 1)
51
+ FROM split
52
+ WHERE instr(rest, '/') > 0
53
+ )
54
+ SELECT directory, count(*) AS files
55
+ FROM split
40
56
  GROUP BY directory
41
- ORDER BY files DESC, directory
42
- LIMIT 50`,
57
+ ORDER BY directory
58
+ LIMIT 200`,
43
59
  )
44
60
  .all(project.id),
45
61
  edgeTypes: database
@@ -81,19 +97,21 @@ export function getProjectOverview(database: TojiDatabase, options: ProjectOverv
81
97
  return overview;
82
98
  }
83
99
 
84
- function findProject(database: TojiDatabase, options: ProjectOverviewOptions): { id: number; root_path: string; name: string; indexed_at: string } | undefined {
100
+ function findProject(database: TojiDatabase, options: ProjectOverviewOptions): { id: number; project_key: string | null; root_path: string; name: string; indexed_at: string } | undefined {
85
101
  if (options.projectId !== undefined) {
86
- return database.query<{ id: number; root_path: string; name: string; indexed_at: string }, [number]>(`SELECT id, root_path, name, indexed_at FROM projects WHERE id = ?`).get(options.projectId);
102
+ return database
103
+ .query<{ id: number; project_key: string | null; root_path: string; name: string; indexed_at: string }, [number | string, string]>(`SELECT id, project_key, root_path, name, indexed_at FROM projects WHERE id = ? OR project_key = ?`)
104
+ .get(options.projectId, String(options.projectId));
87
105
  }
88
106
 
89
107
  if (options.projectPath) {
90
108
  return database
91
- .query<{ id: number; root_path: string; name: string; indexed_at: string }, [string]>(`SELECT id, root_path, name, indexed_at FROM projects WHERE root_path = ?`)
109
+ .query<{ id: number; project_key: string | null; root_path: string; name: string; indexed_at: string }, [string]>(`SELECT id, project_key, root_path, name, indexed_at FROM projects WHERE root_path = ?`)
92
110
  .get(resolve(options.projectPath));
93
111
  }
94
112
 
95
113
  return database
96
- .query<{ id: number; root_path: string; name: string; indexed_at: string }, []>(`SELECT id, root_path, name, indexed_at FROM projects ORDER BY indexed_at DESC, id DESC LIMIT 1`)
114
+ .query<{ id: number; project_key: string | null; root_path: string; name: string; indexed_at: string }, []>(`SELECT id, project_key, root_path, name, indexed_at FROM projects ORDER BY indexed_at DESC, id DESC LIMIT 1`)
97
115
  .get();
98
116
  }
99
117