scai 0.1.17 โ†’ 0.1.18

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.
@@ -0,0 +1,45 @@
1
+ import fg from 'fast-glob';
2
+ import path from 'path';
3
+ import { ModelConfig } from '../config/ModelConfig.js';
4
+ import { initSchema } from '../db/schema.js';
5
+ import { indexFile } from '../db/fileIndex.js';
6
+ import { getSummary } from '../utils/getSummary.js';
7
+ import { EXTENSIONS_BY_LANG } from '../utils/languageConfig.js';
8
+ const IGNORE = [
9
+ '**/node_modules/**',
10
+ '**/dist/**',
11
+ '**/build/**',
12
+ '**/coverage/**',
13
+ '**/.git/**',
14
+ '**/*.test.*'
15
+ ];
16
+ export async function runIndexCommand(targetDir = process.cwd()) {
17
+ console.log(`๐Ÿ“‚ Indexing files in: ${targetDir}`);
18
+ initSchema();
19
+ const lang = ModelConfig.getLanguage();
20
+ const exts = EXTENSIONS_BY_LANG[lang] || ['.txt'];
21
+ const patterns = exts.map(ext => `**/*${ext}`);
22
+ const files = await fg(patterns, {
23
+ cwd: targetDir,
24
+ ignore: IGNORE,
25
+ absolute: true,
26
+ });
27
+ // Count file extensions (diagnostic)
28
+ const countByExt = {};
29
+ files.forEach(file => {
30
+ const ext = path.extname(file);
31
+ countByExt[ext] = (countByExt[ext] || 0) + 1;
32
+ });
33
+ console.log('๐Ÿ“Š Indexed files by extension:', countByExt);
34
+ for (const file of files) {
35
+ try {
36
+ const summary = getSummary(file, lang);
37
+ indexFile(file, summary, lang);
38
+ console.log(`๐Ÿ“„ Indexed: ${path.relative(targetDir, file)}`);
39
+ }
40
+ catch (err) {
41
+ console.warn(`โš ๏ธ Skipped ${file}:`, err instanceof Error ? err.message : err);
42
+ }
43
+ }
44
+ console.log(`โœ… Done. Indexed ${files.length} files.`);
45
+ }
@@ -0,0 +1,27 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { db } from '../db/client.js';
4
+ export function resetDatabase() {
5
+ const dbPath = path.resolve(process.cwd(), '.scai/db.sqlite');
6
+ try {
7
+ db.close(); // ๐Ÿ”’ Make sure the DB is closed
8
+ console.log('๐Ÿ”’ Closed SQLite database connection.');
9
+ }
10
+ catch (err) {
11
+ console.warn('โš ๏ธ Could not close database:', err);
12
+ }
13
+ if (fs.existsSync(dbPath)) {
14
+ try {
15
+ fs.unlinkSync(dbPath);
16
+ console.log('๐Ÿงน Deleted existing database.');
17
+ }
18
+ catch (err) {
19
+ console.error('โŒ Failed to delete DB file:', err instanceof Error ? err.message : err);
20
+ return;
21
+ }
22
+ }
23
+ else {
24
+ console.log('โ„น๏ธ No existing database found.');
25
+ }
26
+ console.log('โœ… Database has been reset. You can now re-run: scai index');
27
+ }
@@ -0,0 +1,7 @@
1
+ import Database from 'better-sqlite3';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ const DB_PATH = path.resolve(process.cwd(), '.scai/db.sqlite');
5
+ // Ensure directory exists
6
+ fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
7
+ export const db = new Database(DB_PATH);
@@ -0,0 +1,18 @@
1
+ import { db } from './client.js';
2
+ export function indexFile(filePath, summary, lang) {
3
+ const insertStmt = db.prepare(`
4
+ INSERT OR REPLACE INTO files (path, summary, lang, indexed_at)
5
+ VALUES (?, ?, ?, datetime('now'))
6
+ `);
7
+ insertStmt.run(filePath, summary, lang);
8
+ }
9
+ export function searchFiles(query, limit = 10) {
10
+ const stmt = db.prepare(`
11
+ SELECT path, summary FROM files
12
+ WHERE path LIKE ? OR summary LIKE ?
13
+ ORDER BY indexed_at DESC
14
+ LIMIT ?
15
+ `);
16
+ const wildcard = `%${query}%`;
17
+ return stmt.all(wildcard, wildcard, limit);
18
+ }
@@ -0,0 +1,13 @@
1
+ import { db } from './client.js';
2
+ export function initSchema() {
3
+ db.exec(`
4
+ CREATE TABLE IF NOT EXISTS files (
5
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ path TEXT UNIQUE,
7
+ summary TEXT,
8
+ lang TEXT,
9
+ indexed_at TEXT
10
+ );
11
+ `);
12
+ console.log('โœ… SQLite schema initialized');
13
+ }
package/dist/index.js CHANGED
@@ -15,7 +15,8 @@ import { ModelConfig } from './config/ModelConfig.js';
15
15
  import { summarizeFile } from "./commands/SummaryCmd.js";
16
16
  import { handleChangelogUpdate } from './commands/ChangeLogUpdateCmd.js';
17
17
  import { runModulePipelineFromCLI } from './commands/ModulePipelineCmd.js';
18
- import { generateProjectContext } from './context/generateProjectContext.js';
18
+ import { runIndexCommand } from './commands/IndexCmd.js';
19
+ import { resetDatabase } from './commands/ResetDbCmd.js';
19
20
  // Create the CLI instance
20
21
  const cmd = new Command('scai')
21
22
  .version(version)
@@ -29,12 +30,6 @@ cmd
29
30
  await bootstrap();
30
31
  console.log('โœ… Model initialization completed!');
31
32
  });
32
- cmd
33
- .command('context')
34
- .description('Generate a summary-based context map of your project')
35
- .action(async () => {
36
- await generateProjectContext(); // Your new scanner module
37
- });
38
33
  cmd
39
34
  .command('sugg')
40
35
  .description('Suggest a commit message from staged changes')
@@ -73,6 +68,16 @@ cmd
73
68
  .action(() => {
74
69
  ModelConfig.logCurrentConfig();
75
70
  });
71
+ cmd
72
+ .command('index [targetDir]')
73
+ .description('Index supported files in the given directory (or current folder if none)')
74
+ .action((targetDir) => {
75
+ runIndexCommand(targetDir);
76
+ });
77
+ cmd
78
+ .command('reset-db')
79
+ .description('Delete and reset the SQLite database')
80
+ .action(() => resetDatabase());
76
81
  cmd
77
82
  .arguments('<file>')
78
83
  .option('-m, --modules <modules>', 'Comma-separated list of modules to run (e.g., comments,cleanup,summary)')
@@ -0,0 +1,31 @@
1
+ // src/utils/getSummary.ts
2
+ import path from 'path';
3
+ export function getSummary(filename, lang) {
4
+ const base = path.basename(filename).toLowerCase();
5
+ const ext = path.extname(base);
6
+ if (base === 'package.json')
7
+ return 'Defines project metadata and dependencies.';
8
+ if (base === 'tsconfig.json')
9
+ return 'TypeScript compiler settings.';
10
+ if (base === 'pyproject.toml')
11
+ return 'Python build and dependency configuration.';
12
+ if (base === 'Cargo.toml')
13
+ return 'Rust project configuration.';
14
+ if (base === 'pom.xml')
15
+ return 'Maven config for a Java project.';
16
+ if (base === 'README.md')
17
+ return 'Project documentation.';
18
+ if (base.startsWith('index'))
19
+ return 'Entry point module.';
20
+ if (lang === 'ts' || lang === 'js') {
21
+ if (base.includes('service'))
22
+ return 'Service logic module.';
23
+ if (base.includes('util'))
24
+ return 'Utility/helper module.';
25
+ if (base.includes('controller'))
26
+ return 'Handles request/response logic.';
27
+ if (base.includes('router'))
28
+ return 'Routing definitions.';
29
+ }
30
+ return `Generic ${ext.replace('.', '')} file.`;
31
+ }
@@ -0,0 +1,7 @@
1
+ export const EXTENSIONS_BY_LANG = {
2
+ ts: ['.ts', '.tsx'],
3
+ js: ['.js', '.jsx'],
4
+ java: ['.java'],
5
+ rust: ['.rs'],
6
+ python: ['.py'],
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"
@@ -24,11 +24,13 @@
24
24
  "start": "node dist/index.js"
25
25
  },
26
26
  "dependencies": {
27
+ "better-sqlite3": "^12.1.1",
27
28
  "commander": "^11.0.0",
28
29
  "fast-glob": "^3.3.3",
29
30
  "ora": "^8.2.0"
30
31
  },
31
32
  "devDependencies": {
33
+ "@types/better-sqlite3": "^7.6.13",
32
34
  "@types/jest": "^30.0.0",
33
35
  "@types/node": "^24.0.1",
34
36
  "jest": "^30.0.2",
@@ -1,104 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
- import fg from 'fast-glob';
4
- import { posix as pathPosix } from 'path';
5
- import { ModelConfig } from '../config/ModelConfig.js';
6
- const IGNORE = ['node_modules', 'dist', 'build', 'coverage', '.git', '**/*.test.*'];
7
- const EXTENSIONS_BY_LANG = {
8
- ts: ['.ts', '.tsx'],
9
- js: ['.js', '.jsx'],
10
- java: ['.java'],
11
- rust: ['.rs'],
12
- python: ['.py'],
13
- };
14
- function getSummary(filename, lang) {
15
- const base = path.basename(filename).toLowerCase();
16
- const ext = path.extname(base);
17
- if (base === 'package.json')
18
- return 'Defines project metadata and dependencies.';
19
- if (base === 'tsconfig.json')
20
- return 'TypeScript compiler settings.';
21
- if (base === 'pyproject.toml')
22
- return 'Python build and dependency configuration.';
23
- if (base === 'Cargo.toml')
24
- return 'Rust project configuration.';
25
- if (base === 'pom.xml')
26
- return 'Maven config for a Java project.';
27
- if (base === 'README.md')
28
- return 'Project documentation.';
29
- if (base.startsWith('index'))
30
- return 'Entry point module.';
31
- if (lang === 'ts' || lang === 'js') {
32
- if (base.includes('service'))
33
- return 'Service logic module.';
34
- if (base.includes('util'))
35
- return 'Utility/helper module.';
36
- if (base.includes('controller'))
37
- return 'Handles request/response logic.';
38
- if (base.includes('router'))
39
- return 'Routing definitions.';
40
- }
41
- if (lang === 'java') {
42
- if (base.includes('controller'))
43
- return 'Java controller class.';
44
- if (base.includes('service'))
45
- return 'Business logic in Java.';
46
- }
47
- if (lang === 'python') {
48
- if (base.includes('main'))
49
- return 'Main execution script.';
50
- if (base.includes('config'))
51
- return 'Configuration or settings.';
52
- }
53
- if (lang === 'rust') {
54
- if (base === 'main.rs')
55
- return 'Main Rust binary entry point.';
56
- if (base === 'lib.rs')
57
- return 'Rust library root module.';
58
- }
59
- return `Generic ${ext.replace('.', '')} file.`;
60
- }
61
- function insertNested(tree, parts, summary) {
62
- const name = parts.pop();
63
- let curr = tree;
64
- for (const dir of parts) {
65
- if (!curr[dir])
66
- curr[dir] = {};
67
- curr = curr[dir];
68
- }
69
- curr[name] = summary;
70
- }
71
- export async function generateProjectContext(root = process.cwd()) {
72
- const lang = ModelConfig.getLanguage();
73
- const extensions = EXTENSIONS_BY_LANG[lang];
74
- const patterns = extensions.map((ext) => `**/*${ext}`);
75
- // Add language-relevant config files
76
- if (lang === 'ts')
77
- patterns.push('tsconfig.json', 'package.json');
78
- if (lang === 'js')
79
- patterns.push('package.json');
80
- if (lang === 'java')
81
- patterns.push('pom.xml');
82
- if (lang === 'python')
83
- patterns.push('pyproject.toml', '*.py');
84
- if (lang === 'rust')
85
- patterns.push('Cargo.toml', '*.rs');
86
- const files = await fg(patterns, {
87
- cwd: root,
88
- ignore: IGNORE,
89
- });
90
- const flat = {};
91
- const tree = {};
92
- for (const file of files) {
93
- const summary = getSummary(file, lang);
94
- flat[file] = summary;
95
- insertNested(tree, pathPosix.normalize(file).split('/'), summary);
96
- }
97
- await fs.mkdir('.scai', { recursive: true });
98
- await fs.writeFile('.scai/context.flat.json', JSON.stringify(flat, null, 2));
99
- await fs.writeFile('.scai/context.tree.json', JSON.stringify(tree, null, 2));
100
- console.log(`โœ… Context generated:
101
- - .scai/context.flat.json
102
- - .scai/context.tree.json
103
- - Language used: ${lang}`);
104
- }
@@ -1 +0,0 @@
1
- "use strict";