@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
|
@@ -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:
|
|
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:
|
|
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:
|
|
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
|
-
`
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
42
|
-
LIMIT
|
|
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
|
|
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
|
|