scai 0.1.58 โ†’ 0.1.60

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.
@@ -3,7 +3,6 @@ import { ancestor as walkAncestor } from 'acorn-walk';
3
3
  import { generateEmbedding } from '../../lib/generateEmbedding.js';
4
4
  import path from 'path';
5
5
  import { log } from '../../utils/log.js';
6
- import fs from 'fs';
7
6
  import { markFileAsSkippedTemplate, markFileAsExtractedTemplate, markFileAsFailedTemplate } from '../sqlTemplates.js';
8
7
  import { getDbForRepo } from '../client.js';
9
8
  function getFunctionName(node, parent, fileName) {
@@ -22,9 +21,8 @@ function getFunctionName(node, parent, fileName) {
22
21
  export async function extractFromJS(filePath, content, fileId) {
23
22
  const db = getDbForRepo();
24
23
  try {
25
- const code = fs.readFileSync(filePath, 'utf-8');
26
24
  console.log(`[Debug] Attempting to parse: ${filePath}`);
27
- console.log(`[Debug] First 3 lines:\n${code.split('\n').slice(0, 3).join('\n')}`);
25
+ console.log(`[Debug] First 3 lines:\n${content.split('\n').slice(0, 3).join('\n')}`);
28
26
  const ast = parse(content, {
29
27
  ecmaVersion: 'latest',
30
28
  sourceType: 'module',
package/dist/index.js CHANGED
@@ -26,6 +26,9 @@ import { promptForToken } from "./github/token.js";
26
26
  import { validateGitHubTokenAgainstRepo } from "./github/githubAuthCheck.js";
27
27
  import { checkGit } from "./commands/GitCmd.js";
28
28
  import { runSwitchCommand, runInteractiveSwitch } from "./commands/SwitchCmd.js";
29
+ import { execSync } from "child_process";
30
+ import { dirname, resolve } from "path";
31
+ import { fileURLToPath } from "url";
29
32
  // ๐ŸŽ›๏ธ CLI Setup
30
33
  const cmd = new Command('scai')
31
34
  .version(version)
@@ -174,6 +177,23 @@ index
174
177
  runInteractiveSwitch();
175
178
  }
176
179
  });
180
+ // This will help resolve the current directory in an ES Module
181
+ cmd
182
+ .command('check-db')
183
+ .description('Run the dbcheck script to check the database status')
184
+ .action(() => {
185
+ const __filename = fileURLToPath(import.meta.url);
186
+ const __dirname = dirname(__filename);
187
+ // Go up two levels from dist/commands โ†’ dist โ†’ then into dist/scripts/dbcheck.js
188
+ const scriptPath = resolve(__dirname, '..', 'dist/scripts', 'dbcheck.js');
189
+ console.log(`๐Ÿš€ Running database check script: ${scriptPath}`);
190
+ try {
191
+ execSync(`node "${scriptPath}"`, { stdio: 'inherit' });
192
+ }
193
+ catch (err) {
194
+ console.error('โŒ Error running dbcheck script:', err instanceof Error ? err.message : err);
195
+ }
196
+ });
177
197
  cmd
178
198
  .command('backup')
179
199
  .description('Backup the current .scai folder')
@@ -0,0 +1,226 @@
1
+ import Database from "better-sqlite3";
2
+ import path from "path";
3
+ import os from "os";
4
+ import { Config } from "../config.js";
5
+ import fs from "fs";
6
+ const cfg = Config.getRaw();
7
+ const repoKey = cfg.activeRepo;
8
+ if (!repoKey) {
9
+ console.error("โŒ No active repo found. Use `scai set-index` to set one.");
10
+ process.exit(1);
11
+ }
12
+ // Get the basename (repo name) from the full repoKey (which is the path)
13
+ const repoName = path.basename(repoKey);
14
+ const scaiRepoRoot = path.join(os.homedir(), ".scai", "repos", repoName);
15
+ const dbPath = path.join(scaiRepoRoot, "db.sqlite");
16
+ if (!fs.existsSync(dbPath)) {
17
+ console.error(`โŒ No database found at ${dbPath}`);
18
+ process.exit(1);
19
+ }
20
+ const db = new Database(dbPath);
21
+ // === Basic Stats ===
22
+ const stats = {
23
+ total: db.prepare('SELECT COUNT(*) as count FROM files').get().count,
24
+ withSummary: db.prepare(`SELECT COUNT(*) as count FROM files WHERE summary IS NOT NULL AND summary != ''`).get().count,
25
+ withoutSummary: db.prepare(`SELECT COUNT(*) as count FROM files WHERE summary IS NULL OR summary = ''`).get().count,
26
+ withEmbedding: db.prepare(`SELECT COUNT(*) as count FROM files WHERE embedding IS NOT NULL AND embedding != ''`).get().count,
27
+ withoutEmbedding: db.prepare(`SELECT COUNT(*) as count FROM files WHERE embedding IS NULL OR embedding = ''`).get().count,
28
+ withProcessingStatusExtracted: db.prepare(`SELECT COUNT(*) as count FROM files WHERE processing_status = 'extracted'`).get().count,
29
+ withoutProcessingStatusExtracted: db.prepare(`SELECT COUNT(*) as count FROM files WHERE processing_status IS NULL OR processing_status != 'extracted'`).get().count,
30
+ withProcessingStatusSkipped: db.prepare(`SELECT COUNT(*) as count FROM files WHERE processing_status = 'skipped'`).get().count,
31
+ withProcessingStatusFailed: db.prepare(`SELECT COUNT(*) as count FROM files WHERE processing_status = 'failed'`).get().count,
32
+ withProcessingStatusUnprocessed: db.prepare(`SELECT COUNT(*) as count FROM files WHERE processing_status = 'unprocessed'`).get().count,
33
+ };
34
+ console.log("๐Ÿ“Š SQLite Stats for Table: files");
35
+ console.log("-------------------------------------------");
36
+ console.log(`๐Ÿ”ข Total rows: ${stats.total}`);
37
+ console.log(`โœ… With summary: ${stats.withSummary}`);
38
+ console.log(`โŒ Without summary: ${stats.withoutSummary}`);
39
+ console.log(`โœ… With embedding: ${stats.withEmbedding}`);
40
+ console.log(`โŒ Without embedding: ${stats.withoutEmbedding}`);
41
+ console.log(`โœ… With processing_status = 'extracted': ${stats.withProcessingStatusExtracted}`);
42
+ console.log(`โŒ Without processing_status = 'extracted': ${stats.withoutProcessingStatusExtracted}`);
43
+ console.log(`โœ… With processing_status = 'skipped': ${stats.withProcessingStatusSkipped}`);
44
+ console.log(`โœ… With processing_status = 'failed': ${stats.withProcessingStatusFailed}`);
45
+ console.log(`โœ… With processing_status = 'unprocessed': ${stats.withProcessingStatusUnprocessed}`);
46
+ // === Example Summaries ===
47
+ console.log("\n๐Ÿงพ Example summaries and embeddings:\n--------------------------");
48
+ console.log("Example summaries (first 50 characters):");
49
+ const summaries = db.prepare(`
50
+ SELECT id, substr(summary, 1, 50) || '...' AS short_summary
51
+ FROM files
52
+ WHERE summary IS NOT NULL AND summary != ''
53
+ LIMIT 10
54
+ `).all();
55
+ summaries.forEach(row => {
56
+ console.log(`${row.id.toString().padEnd(4)} ${row.short_summary}`);
57
+ });
58
+ console.log("\nExample embeddings (first 50 characters):");
59
+ const embeddings = db.prepare(`
60
+ SELECT id, substr(embedding, 1, 50) || '...' AS short_embedding
61
+ FROM files
62
+ WHERE embedding IS NOT NULL AND embedding != ''
63
+ LIMIT 10
64
+ `).all();
65
+ embeddings.forEach(row => {
66
+ console.log(`${row.id.toString().padEnd(4)} ${row.short_embedding}`);
67
+ });
68
+ // === FTS5 Check ===
69
+ console.log("\n๐Ÿ” FTS5 Check");
70
+ console.log("--------------------------");
71
+ try {
72
+ const ftsExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='files_fts'`).get();
73
+ if (!ftsExists) {
74
+ console.log("โŒ No FTS5 table `files_fts` found in database.");
75
+ }
76
+ else {
77
+ const ftsRowCount = db.prepare(`SELECT COUNT(*) AS count FROM files_fts`).get().count;
78
+ console.log(`โœ… files_fts table exists. Rows: ${ftsRowCount}`);
79
+ }
80
+ }
81
+ catch (err) {
82
+ console.error("โŒ Error while accessing files_fts:", err.message);
83
+ }
84
+ // === Rebuild FTS Index ===
85
+ console.log("\n๐Ÿ”ง Rebuilding FTS5 index...");
86
+ try {
87
+ db.prepare(`INSERT INTO files_fts(files_fts) VALUES ('rebuild')`).run();
88
+ console.log(`โœ… Rebuild completed.`);
89
+ }
90
+ catch (err) {
91
+ console.error("โŒ FTS5 rebuild failed:", err.message);
92
+ }
93
+ // === FTS Search Test ===
94
+ console.log('\n๐Ÿ” Test MATCH query for "minimap":');
95
+ try {
96
+ const minimapMatches = db.prepare(`
97
+ SELECT f.id, f.path
98
+ FROM files f
99
+ JOIN files_fts fts ON f.id = fts.rowid
100
+ WHERE fts.files_fts MATCH 'minimap'
101
+ LIMIT 10
102
+ `).all();
103
+ if (minimapMatches.length === 0) {
104
+ console.warn('โš ๏ธ No matches for "minimap" in FTS index');
105
+ }
106
+ else {
107
+ minimapMatches.forEach(row => {
108
+ console.log(`๐Ÿ“„ ${row.path}`);
109
+ });
110
+ }
111
+ }
112
+ catch (err) {
113
+ console.error('โŒ Error running MATCH query:', err.message);
114
+ }
115
+ // === Direct LIKE Fallback ===
116
+ console.log('\n๐Ÿ” Direct LIKE query on path for "minimap":');
117
+ const likeMatches = db.prepare(`
118
+ SELECT id, path
119
+ FROM files
120
+ WHERE path LIKE '%minimap%'
121
+ LIMIT 10
122
+ `).all();
123
+ if (likeMatches.length === 0) {
124
+ console.warn('โš ๏ธ No file paths contain "minimap"');
125
+ }
126
+ else {
127
+ likeMatches.forEach(row => {
128
+ console.log(`๐Ÿ“„ ${row.path}`);
129
+ });
130
+ }
131
+ // === Function Table Stats ===
132
+ console.log('\n๐Ÿ“Š Stats for Table: functions');
133
+ console.log('-------------------------------------------');
134
+ try {
135
+ const funcCount = db.prepare(`SELECT COUNT(*) AS count FROM functions`).get().count;
136
+ const distinctFiles = db.prepare(`SELECT COUNT(DISTINCT file_id) AS count FROM functions`).get().count;
137
+ console.log(`๐Ÿ”ข Total functions: ${funcCount}`);
138
+ console.log(`๐Ÿ“‚ Distinct files: ${distinctFiles}`);
139
+ }
140
+ catch (err) {
141
+ console.error('โŒ Error accessing functions table:', err.message);
142
+ }
143
+ // === Example Functions ===
144
+ console.log('\n๐Ÿงช Example extracted functions:');
145
+ try {
146
+ const sampleFunctions = db.prepare(`
147
+ SELECT id, name, start_line, end_line, substr(content, 1, 100) || '...' AS short_body
148
+ FROM functions
149
+ ORDER BY id DESC
150
+ LIMIT 5
151
+ `).all();
152
+ sampleFunctions.forEach(fn => {
153
+ console.log(`๐Ÿ”น ID: ${fn.id}`);
154
+ console.log(` Name: ${fn.name}`);
155
+ console.log(` Lines: ${fn.start_line}-${fn.end_line}`);
156
+ console.log(` Body: ${fn.short_body}\n`);
157
+ });
158
+ }
159
+ catch (err) {
160
+ console.error('โŒ Error printing function examples:', err.message);
161
+ }
162
+ // === Function Calls Table Stats ===
163
+ console.log('\n๐Ÿ“Š Stats for Table: function_calls');
164
+ console.log('-------------------------------------------');
165
+ try {
166
+ const callCount = db.prepare(`SELECT COUNT(*) AS count FROM function_calls`).get().count;
167
+ const topCallers = db.prepare(`
168
+ SELECT caller_id, COUNT(*) AS num_calls
169
+ FROM function_calls
170
+ GROUP BY caller_id
171
+ ORDER BY num_calls DESC
172
+ LIMIT 5
173
+ `).all();
174
+ console.log(`๐Ÿ” Total function calls: ${callCount}`);
175
+ console.log('๐Ÿ“ž Top callers:');
176
+ topCallers.forEach(row => {
177
+ console.log(` - Caller ${row.caller_id} made ${row.num_calls} calls`);
178
+ });
179
+ }
180
+ catch (err) {
181
+ console.error('โŒ Error accessing function_calls table:', err.message);
182
+ }
183
+ // === Random Summary Samples ===
184
+ console.log('\n๐Ÿงพ 10 Random Summaries (ID + Preview):');
185
+ console.log('-------------------------------------------');
186
+ const randomSummaries = db.prepare(`
187
+ SELECT id, filename, substr(summary, 1, 1000) || '...' AS preview
188
+ FROM files
189
+ WHERE summary IS NOT NULL AND summary != ''
190
+ ORDER BY RANDOM()
191
+ LIMIT 10
192
+ `).all();
193
+ randomSummaries.forEach(row => {
194
+ console.log(`๐Ÿ“„ [${row.id}] ${row.filename}: ${row.preview}`);
195
+ });
196
+ // === Random Functions Samples ===
197
+ console.log('\n๐Ÿง‘โ€๐Ÿ’ป 20 Random Functions (ID, name, body):');
198
+ console.log('-------------------------------------------');
199
+ const randomFunctions = db.prepare(`
200
+ SELECT id, name, file_id, substr(content, 1, 100) || '...' AS preview
201
+ FROM functions
202
+ ORDER BY RANDOM()
203
+ LIMIT 20
204
+ `).all();
205
+ randomFunctions.forEach(row => {
206
+ console.log(`๐Ÿ”น [${row.id}] ${row.name} (file_id: ${row.file_id})`);
207
+ console.log(` ${row.preview}\n`);
208
+ });
209
+ // === Column View of 100 Files ===
210
+ console.log('\n๐Ÿ“Š Table View: First 100 Files');
211
+ console.log('-------------------------------------------');
212
+ const fileRows = db.prepare(`
213
+ SELECT id, filename, type, processing_status, functions_extracted_at, length(summary) AS summary_len
214
+ FROM files
215
+ LIMIT 1000
216
+ `).all();
217
+ console.table(fileRows);
218
+ // === Column View of 100 Functions ===
219
+ console.log('\n๐Ÿ“Š Table View: First 100 Functions');
220
+ console.log('-------------------------------------------');
221
+ const functionRows = db.prepare(`
222
+ SELECT id, file_id, name, start_line, end_line, length(content) AS length
223
+ FROM functions
224
+ LIMIT 1000
225
+ `).all();
226
+ console.table(functionRows);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"
@@ -50,10 +50,11 @@
50
50
  "devDependencies": {
51
51
  "@types/better-sqlite3": "^7.6.13",
52
52
  "@types/jest": "^30.0.0",
53
- "@types/node": "^24.0.13",
53
+ "@types/node": "^24.1.0",
54
54
  "@types/proper-lockfile": "^4.1.4",
55
55
  "jest": "^30.0.2",
56
56
  "ts-jest": "^29.4.0",
57
+ "ts-node": "^10.9.2",
57
58
  "typescript": "^5.8.3"
58
59
  },
59
60
  "files": [