scai 0.1.26 → 0.1.27

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.
@@ -1,20 +1,21 @@
1
1
  import { queryFiles } from '../db/fileIndex.js';
2
+ import { sanitizeQueryForFts } from '../utils/sanitizeQuery.js';
2
3
  import path from 'path';
3
- export async function runQueryCommand(query) {
4
+ export async function runFindCommand(query) {
4
5
  if (!query) {
5
- console.error('❌ Please provide a search query.\n👉 Usage: scai query "keyword"');
6
+ console.error('❌ Please provide a search query.\n👉 Usage: scai find "keyword"');
6
7
  return;
7
8
  }
8
- console.log(`🔍 Searching for: "${query}"\n`);
9
- const results = queryFiles(query);
9
+ console.log(`\n🔍 Searching for: "${query}"\n`);
10
+ const sanitizedQuery = sanitizeQueryForFts(query);
11
+ const results = queryFiles(sanitizedQuery);
10
12
  if (results.length === 0) {
11
13
  console.log('⚠️ No matching files found.');
12
14
  return;
13
15
  }
16
+ console.log(`✅ Found ${results.length} result(s).`);
17
+ console.log();
14
18
  results.forEach((result, index) => {
15
19
  console.log(`📄 [${index + 1}] ${path.relative(process.cwd(), result.path)}`);
16
- console.log(` 📝 ${result.summary}`);
17
- console.log();
18
20
  });
19
- console.log(`✅ Found ${results.length} result(s).`);
20
21
  }
@@ -1,44 +1,75 @@
1
1
  import fs from 'fs/promises';
2
+ import path from 'path';
2
3
  import readline from 'readline';
3
- import { summaryModule } from '../pipeline/modules/summaryModule.js'; // Import summaryModule
4
- import { summarizeCode } from '../utils/summarizer.js'; // Import summarizeCode
4
+ import { queryFiles } from '../db/fileIndex.js';
5
+ import { summaryModule } from '../pipeline/modules/summaryModule.js';
6
+ import { summarizeCode } from '../utils/summarizer.js';
5
7
  export async function summarizeFile(filepath) {
6
8
  let content = '';
7
- if (filepath) {
9
+ let summary;
10
+ let filePathResolved = filepath ? path.resolve(process.cwd(), filepath) : undefined;
11
+ // Handle case where user provides only a filename (without extension)
12
+ if (filepath && !path.extname(filepath)) {
13
+ // Search for matching files using the provided base name
14
+ const matches = queryFiles(`"${filepath}"`);
15
+ if (matches.length > 0) {
16
+ const match = matches[0]; // Get the first match (adjust based on your preference)
17
+ filePathResolved = path.resolve(process.cwd(), match.path);
18
+ }
19
+ }
20
+ else if (filepath && path.extname(filepath)) {
21
+ // Handle case where full filename with extension is provided
22
+ filePathResolved = path.resolve(process.cwd(), filepath);
23
+ }
24
+ // Now, let's search the database for a summary
25
+ if (filePathResolved) {
8
26
  try {
9
- content = await fs.readFile(filepath, 'utf-8');
27
+ // Try to find an existing summary from the database using the resolved path
28
+ const matches = queryFiles(`"${filePathResolved}"`);
29
+ const match = matches.find(row => path.resolve(row.path) === filePathResolved);
30
+ if (match?.summary) {
31
+ // If a summary exists in the database, use it
32
+ console.log(`🧠 Cached summary for ${filepath}:\n`);
33
+ console.log(summarizeCode(match.summary));
34
+ return;
35
+ }
36
+ // If no cached summary, read the file content
37
+ content = await fs.readFile(filePathResolved, 'utf-8');
10
38
  }
11
39
  catch (err) {
12
- console.error(`❌ Could not read or summarize ${filepath}:`, err.message);
40
+ console.error(`❌ Could not process ${filepath}:`, err.message);
13
41
  return;
14
42
  }
15
43
  }
16
- else if (process.stdin.isTTY) {
17
- console.error('❌ No file provided and no piped input.\n👉 Usage: scai summ <file> or cat file | scai summ');
18
- return;
19
- }
20
- else {
44
+ else if (!process.stdin.isTTY) {
45
+ // If no file path and input comes from stdin (piped content)
21
46
  const rl = readline.createInterface({
22
47
  input: process.stdin,
23
48
  output: process.stdout,
24
49
  terminal: false,
25
50
  });
51
+ // Collect all piped input into the `content` string
26
52
  for await (const line of rl) {
27
53
  content += line + '\n';
28
54
  }
29
55
  }
56
+ else {
57
+ console.error('❌ No file provided and no piped input.\n👉 Usage: scai summ <file> or cat file | scai summ');
58
+ return;
59
+ }
60
+ // If content is available (from file or stdin)
30
61
  if (content.trim()) {
31
- // Call the summary module to get the raw summary
62
+ console.log('🧪 Generating summary...\n');
63
+ // Generate a summary using your summarization pipeline
32
64
  const response = await summaryModule.run({ content, filepath });
33
- // Pass the summary text to the utility function for formatting
34
65
  if (!response.summary) {
35
- console.warn("No summary available.");
66
+ console.warn('⚠️ No summary generated.');
36
67
  return;
37
68
  }
38
- const formattedSummary = summarizeCode(response.summary);
39
- console.log(formattedSummary);
69
+ // Print the formatted summary
70
+ console.log(summarizeCode(response.summary));
40
71
  }
41
72
  else {
42
- console.error('❌ No code provided to summarize.');
73
+ console.error('❌ No content provided to summarize.');
43
74
  }
44
75
  }
@@ -29,15 +29,7 @@ export function indexFile(filePath, summary, type) {
29
29
  /**
30
30
  * Perform a raw keyword-based full-text search using the FTS5 index.
31
31
  */
32
- export function queryFiles(query, limit = 10) {
33
- const safeQuery = query
34
- .trim()
35
- .split(/\s+/)
36
- .map(token => {
37
- token = token.replace(/[?*\\"]/g, '').replace(/'/g, "''");
38
- return token.includes(' ') ? `"${token}"` : `${token}*`;
39
- })
40
- .join(' OR ');
32
+ export function queryFiles(safeQuery, limit = 10) {
41
33
  console.log(`Executing search query: ${safeQuery}`);
42
34
  const results = db.prepare(`
43
35
  SELECT f.id, f.path, f.summary, f.type, f.last_modified, f.indexed_at
@@ -46,10 +38,6 @@ export function queryFiles(query, limit = 10) {
46
38
  WHERE fts.files_fts MATCH ?
47
39
  LIMIT ?
48
40
  `).all(safeQuery, limit);
49
- console.log(`Search returned ${results.length} results.`);
50
- results.forEach(result => {
51
- console.log(`📄 Found in FTS search: ${result.path}`);
52
- });
53
41
  return results;
54
42
  }
55
43
  /**
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { Config } from './config.js';
5
5
  import { createRequire } from 'module';
6
6
  const require = createRequire(import.meta.url);
7
7
  const { version } = require('../package.json');
8
+ // 🧠 Commands
8
9
  import { suggestCommitMessage } from "./commands/CommitSuggesterCmd.js";
9
10
  import { handleRefactor } from "./commands/RefactorCmd.js";
10
11
  import { generateTests } from "./commands/TestGenCmd.js";
@@ -14,7 +15,7 @@ import { handleChangelogUpdate } from './commands/ChangeLogUpdateCmd.js';
14
15
  import { runModulePipelineFromCLI } from './commands/ModulePipelineCmd.js';
15
16
  import { runIndexCommand } from './commands/IndexCmd.js';
16
17
  import { resetDatabase } from './commands/ResetDbCmd.js';
17
- import { runQueryCommand } from './commands/QueryCmd.js';
18
+ import { runFindCommand } from './commands/FindCmd.js';
18
19
  import { startDaemon } from './commands/DaemonCmd.js';
19
20
  import { runStopDaemonCommand } from "./commands/StopDaemonCmd.js";
20
21
  import { runAskCommand } from './commands/AskCmd.js';
@@ -113,9 +114,9 @@ cmd
113
114
  });
114
115
  // 🧠 Query and assistant
115
116
  cmd
116
- .command('query <query>')
117
+ .command('find <query>')
117
118
  .description('Search indexed files by keyword')
118
- .action(runQueryCommand);
119
+ .action(runFindCommand);
119
120
  cmd
120
121
  .command('ask [question...]') // <- the ... makes it variadic
121
122
  .description('Ask a question based on indexed files')
@@ -1,16 +1,19 @@
1
1
  // src/utils/sanitizeQuery.ts
2
2
  import { STOP_WORDS } from '../config/StopWords.js';
3
3
  export function sanitizeQueryForFts(input) {
4
+ input = input.trim().toLowerCase();
5
+ // If it's a single filename-like string (includes dots or slashes), quote it
6
+ if (/^[\w\-./]+$/.test(input) && !/\s/.test(input)) {
7
+ // Escape quotes and wrap with double-quotes for FTS safety
8
+ return `"${input.replace(/"/g, '""')}"*`;
9
+ }
10
+ // Otherwise, treat it as a natural language prompt
4
11
  const tokens = input
5
- .trim()
6
12
  .split(/\s+/)
7
13
  .map(token => token.toLowerCase())
8
14
  .filter(token => token.length > 2 &&
9
15
  !STOP_WORDS.has(token) &&
10
16
  /^[a-z0-9]+$/.test(token))
11
17
  .map(token => token.replace(/[?*\\"]/g, '').replace(/'/g, "''") + '*');
12
- // 👇 Prevent FTS syntax errors by returning a catch-all query
13
- if (tokens.length === 0)
14
- return '*';
15
- return tokens.join(' OR ');
18
+ return tokens.length > 0 ? tokens.join(' OR ') : '*';
16
19
  }
@@ -3,6 +3,14 @@ export function wrapText(text, maxWidth) {
3
3
  let wrappedText = '';
4
4
  let currentLine = '';
5
5
  words.forEach(word => {
6
+ // If the word is longer than the maxWidth, break it up into multiple lines
7
+ if (word.length > maxWidth) {
8
+ // Break the word into smaller chunks
9
+ while (word.length > maxWidth) {
10
+ wrappedText += word.slice(0, maxWidth) + '\n';
11
+ word = word.slice(maxWidth);
12
+ }
13
+ }
6
14
  // Check if adding the word would exceed the max width
7
15
  if ((currentLine + word).length > maxWidth) {
8
16
  wrappedText += currentLine + '\n'; // Add the current line and start a new one
@@ -13,6 +21,6 @@ export function wrapText(text, maxWidth) {
13
21
  }
14
22
  });
15
23
  // Append the last line if any
16
- wrappedText += currentLine;
24
+ wrappedText += currentLine.trim(); // trim() to remove the extra space at the end
17
25
  return wrappedText;
18
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"
@@ -1,10 +0,0 @@
1
- export function checkEnv() {
2
- const requiredVars = ["DB_HOST", "API_KEY"];
3
- const missing = requiredVars.filter((v) => !process.env[v]);
4
- if (missing.length) {
5
- console.warn("❌ Missing env vars:", missing.join(", "));
6
- }
7
- else {
8
- console.log("✅ All env vars are set");
9
- }
10
- }