scai 0.1.91 → 0.1.93

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/README.md CHANGED
@@ -226,14 +226,24 @@ This will:
226
226
 
227
227
  ```bash
228
228
  scai gen summ <file>
229
- scai gen comm <file>
229
+ scai gen comm <file|folder...>
230
230
  scai gen changelog
231
231
  scai gen tests <file>
232
232
  ```
233
233
 
234
234
  * `summ`: Summarize a file
235
- * `comm`: Add comments to a file
235
+
236
+ * `comm`: Add comments to one or more files, or to all matching files in a folder (recursive).
237
+ **Example:**
238
+
239
+ ```bash
240
+ scai gen comm src/ utils/helpers.ts
241
+ ```
242
+
243
+ This will add comments to all `.ts` and `.js` files under `src/` and to `utils/helpers.ts`.
244
+
236
245
  * `changelog`: Update or create `CHANGELOG.md` from Git diff
246
+
237
247
  * `tests`: Create Jest test stubs (ALPHA)
238
248
 
239
249
  You can also pipe file content directly:
@@ -242,6 +252,7 @@ You can also pipe file content directly:
242
252
  cat src/utils/math.ts | scai gen summ
243
253
  ```
244
254
 
255
+
245
256
  ---
246
257
 
247
258
  ## ⚙️ Configuration
package/dist/CHANGELOG.md CHANGED
@@ -117,4 +117,12 @@ Type handling with the module pipeline
117
117
  ## 2025-08-18
118
118
 
119
119
  • Add interactive delete repository command
120
- • Refactor DeleteIndex command to handle repository deletion correctly
120
+ • Refactor DeleteIndex command to handle repository deletion correctly
121
+
122
+ ## 2025-08-19
123
+
124
+ * Improved line classification and merging logic
125
+
126
+ ## 2025-08-19
127
+
128
+ • Update `gen` command's `comm` subcommand to allow targeting files or folders
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ import { addCommentsModule } from './pipeline/modules/commentModule.js';
31
31
  import { generateTestsModule } from './pipeline/modules/generateTestsModule.js';
32
32
  import { preserveCodeModule } from './pipeline/modules/preserveCodeModule.js';
33
33
  import { runInteractiveDelete } from './commands/DeleteIndex.js';
34
+ import { resolveTargetsToFiles } from './utils/resolveTargetsToFiles.js';
34
35
  // 🎛️ CLI Setup
35
36
  const cmd = new Command('scai')
36
37
  .version(version)
@@ -104,10 +105,13 @@ auth
104
105
  // 🛠️ Group: `gen` commands for content generation
105
106
  const gen = cmd.command('gen').description('Generate code-related output');
106
107
  gen
107
- .command('comm <file>')
108
- .description('Write comments for the given file')
109
- .action((file) => {
110
- handleAgentRun(file, [addCommentsModule, preserveCodeModule]);
108
+ .command("comm <targets...>")
109
+ .description("Write comments for the given file(s) or folder(s)")
110
+ .action(async (targets) => {
111
+ const files = await resolveTargetsToFiles(targets, [".ts", ".js"]);
112
+ for (const file of files) {
113
+ await handleAgentRun(file, [addCommentsModule, preserveCodeModule]);
114
+ }
111
115
  });
112
116
  gen
113
117
  .command('changelog')
@@ -57,53 +57,48 @@ export const preserveCodeModule = {
57
57
  const fixedLines = [];
58
58
  let origIndex = 0;
59
59
  let newIndex = 0;
60
- while (origIndex < origLines.length && newIndex < newLines.length) {
60
+ // Track all inserted comment blocks globally
61
+ const insertedBlocks = new Set();
62
+ while (origIndex < origLines.length) {
61
63
  const origLine = origLines[origIndex];
62
- const newLine = newLines[newIndex];
63
- // If either current line is a comment treat whole comment block
64
- let lastInsertedModelBlock = [];
65
- if (classifyLine(origLine) !== "code" || classifyLine(newLine) !== "code") {
64
+ // If this is a comment line in original or model
65
+ if (classifyLine(origLine) !== "code" || classifyLine(newLines[newIndex] ?? "") !== "code") {
66
66
  const origBlock = collectBlock(origLines, origIndex);
67
67
  const modelBlock = collectBlock(newLines, newIndex);
68
- // Compare with last inserted block
69
- if (!blocksEqual(modelBlock, lastInsertedModelBlock)) {
70
- if (blocksEqual(origBlock, modelBlock)) {
71
- fixedLines.push(...origBlock);
72
- }
73
- else {
74
- const seen = new Set(trimBlock(modelBlock));
75
- fixedLines.push(...modelBlock);
76
- for (const line of origBlock) {
77
- if (!seen.has(line.trim())) {
78
- fixedLines.push(line);
79
- }
80
- }
68
+ // Merge: model block first, then any orig lines not in model
69
+ const seen = new Set(trimBlock(modelBlock));
70
+ const mergedBlock = [...modelBlock];
71
+ for (const line of origBlock) {
72
+ if (!seen.has(line.trim())) {
73
+ mergedBlock.push(line);
81
74
  }
82
- // Update lastInsertedModelBlock
83
- lastInsertedModelBlock = [...modelBlock];
75
+ }
76
+ // Create a key for duplicate detection
77
+ const mergedKey = JSON.stringify(trimBlock(mergedBlock));
78
+ // Insert only if this block was never inserted before
79
+ if (!insertedBlocks.has(mergedKey)) {
80
+ fixedLines.push(...mergedBlock);
81
+ insertedBlocks.add(mergedKey);
84
82
  }
85
83
  else {
86
84
  console.log("Skipping duplicate block (already inserted)");
87
85
  }
86
+ // Advance indices past the entire blocks
88
87
  origIndex += origBlock.length;
89
88
  newIndex += modelBlock.length;
90
89
  continue;
91
90
  }
92
- // Non-comment lines
93
- if (origLine.trim() === newLine.trim()) {
94
- fixedLines.push(newLine);
95
- }
96
- else {
97
- fixedLines.push(origLine);
98
- }
91
+ // Non-comment line
92
+ const newLine = newLines[newIndex] ?? "";
93
+ fixedLines.push(origLine.trim() === newLine.trim() ? newLine : origLine);
99
94
  origIndex++;
100
95
  newIndex++;
101
96
  }
102
- // Add trailing lines from original (if model ran out first)
97
+ // Add any remaining original lines if model ran out
103
98
  while (origIndex < origLines.length) {
104
99
  fixedLines.push(origLines[origIndex++]);
105
100
  }
106
- // Add trailing comments from model
101
+ // Add trailing comments from model if any
107
102
  while (newIndex < newLines.length) {
108
103
  if (classifyLine(newLines[newIndex]) !== "code") {
109
104
  fixedLines.push(newLines[newIndex]);
@@ -0,0 +1,29 @@
1
+ import fs from "fs/promises";
2
+ import path from "path";
3
+ export async function resolveTargetsToFiles(targets, exts) {
4
+ const files = [];
5
+ for (const target of targets) {
6
+ const stat = await fs.stat(target);
7
+ if (stat.isDirectory()) {
8
+ files.push(...await collectFilesRecursive(target, exts));
9
+ }
10
+ else {
11
+ files.push(target);
12
+ }
13
+ }
14
+ return files;
15
+ }
16
+ async function collectFilesRecursive(dir, exts) {
17
+ const entries = await fs.readdir(dir, { withFileTypes: true });
18
+ const files = [];
19
+ for (const entry of entries) {
20
+ const fullPath = path.join(dir, entry.name);
21
+ if (entry.isDirectory()) {
22
+ files.push(...await collectFilesRecursive(fullPath, exts));
23
+ }
24
+ else if (exts.includes(path.extname(entry.name))) {
25
+ files.push(fullPath);
26
+ }
27
+ }
28
+ return files;
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.91",
3
+ "version": "0.1.93",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"