scai 0.1.126 → 0.1.127

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,6 +1,5 @@
1
1
  import { builtInModules } from "../pipeline/registry/moduleRegistry.js";
2
2
  import { logInputOutput } from "../utils/promptLogHelper.js";
3
- import { planResolverStep } from "./planResolverStep.js";
4
3
  import { infoPlanGen } from "./infoPlanGenStep.js";
5
4
  import { understandIntentStep } from "./understandIntentStep.js";
6
5
  import { structuralAnalysisStep } from "./structuralAnalysisStep.js";
@@ -13,6 +12,7 @@ import { selectRelevantSourcesStep } from "./selectRelevantSourcesStep.js";
13
12
  import { transformPlanGenStep } from "./transformPlanGenStep.js";
14
13
  import { finalPlanGenStep } from "./finalPlanGenStep.js";
15
14
  import { Spinner } from "../lib/spinner.js";
15
+ import { getDbForRepo } from "../db/client.js";
16
16
  /* ───────────────────────── helpers ───────────────────────── */
17
17
  function startTimer() {
18
18
  const start = Date.now();
@@ -98,26 +98,17 @@ export class MainAgent {
98
98
  const t = startTimer();
99
99
  await understandIntentStep.run({ context: this.context });
100
100
  logLine("BOOT", "understandIntent", t());
101
- }
102
- /* ROUTE */
103
- {
104
- const t = startTimer();
105
- await planResolverStep.run(this.context);
106
- logLine("ROUTE", "planResolver", t());
107
- }
108
- const routing = this.context.analysis?.routingDecision;
109
- if (routing?.decision === "final-answer" && routing.answer) {
110
- this.spinner.stop();
111
- logLine("RUN", "complete", stopRun());
112
- userOutput("All input/output logs can be found at ~/.scai/input_output.log");
113
- return {
114
- query: this.query,
115
- data: {
116
- finalAnswer: routing.answer,
117
- source: "planResolver"
118
- },
119
- context: this.context
120
- };
101
+ // >>> TASK PERSISTENCE ADDITION <<<
102
+ // create a task immediately after boot
103
+ const db = getDbForRepo();
104
+ const now = new Date().toISOString();
105
+ const userQuery = this.context.initContext?.userQuery ?? "unknown query";
106
+ const result = db.prepare(`
107
+ INSERT INTO tasks (initial_query, created_at, updated_at)
108
+ VALUES (?, ?, ?)
109
+ `).run(userQuery, now, now);
110
+ this.taskId = result.lastInsertRowid;
111
+ logLine("TASK", `created task id=${this.taskId}"`);
121
112
  }
122
113
  // =====================================================
123
114
  // INFORMATION ACQUISITION PHASE
@@ -60,10 +60,21 @@ Example format:
60
60
  ? genOutput.data
61
61
  : JSON.stringify(genOutput.data ?? '{}');
62
62
  const cleaned = await cleanupModule.run({ query: intentText, content: raw });
63
- const jsonString = typeof cleaned.content === 'string'
64
- ? cleaned.content
65
- : JSON.stringify(cleaned.content ?? '{}');
66
- let plan = JSON.parse(jsonString);
63
+ let plan = null;
64
+ if (cleaned.data && typeof cleaned.data === 'object') {
65
+ // cleanup succeeded
66
+ plan = cleaned.data;
67
+ }
68
+ else if (typeof cleaned.data === 'string') {
69
+ // ⚠️ fallback: try extracting JSON
70
+ const match = cleaned.data.match(/\{[\s\S]*\}/);
71
+ if (match) {
72
+ plan = JSON.parse(match[0]);
73
+ }
74
+ }
75
+ if (!plan || !Array.isArray(plan.steps)) {
76
+ throw new Error('Invalid final plan structure');
77
+ }
67
78
  if (!plan || !Array.isArray(plan.steps))
68
79
  throw new Error('Invalid final plan structure');
69
80
  if (plan.steps.length > MAX_STEPS)
@@ -70,7 +70,6 @@ Rules:
70
70
  }
71
71
  `.trim();
72
72
  try {
73
- console.dir("planGeneratorPrompt: ", prompt);
74
73
  const genInput = { query: intentText ?? '', content: prompt };
75
74
  const genOutput = await generate(genInput);
76
75
  const llmOutput = typeof genOutput.data === 'string'
@@ -31,8 +31,8 @@ or modifications in the system to achieve the intended task.
31
31
  Intent / task description:
32
32
  ${intentText}
33
33
 
34
- If the intent indicates that this is NOT a coding, refactoring, or inline commenting task,
35
- then return an empty plan object with an empty "steps" array:
34
+ If the intent indicates that this is NOT a coding, refactoring, or inline commenting task,
35
+ then return an empty plan object with an empty "steps" array:
36
36
  { "steps": [] }
37
37
 
38
38
  Allowed actions (transformation only):
@@ -50,12 +50,11 @@ ${JSON.stringify(context.analysis.focus?.relevantFiles ?? {}, null, 2)}
50
50
  Only perform transformations that are safe based on the existing analysis.
51
51
 
52
52
  ⚡ Phase guidance:
53
- - Actions are grouped into phases: info, transform, finalize.
54
53
  - Only include transform steps in this phase.
55
54
  - Include a 'writeFile' step for each 'codeTransform' to persist changes to disk.
56
55
  - Each step must include: "action", "targetFile" (optional), "description", "metadata"
57
56
 
58
- ❌ IMPORTANT: Do NOT include "info" steps here. Only plan actual code transformations.
57
+ ❌ IMPORTANT: Do NOT include "info" steps here.
59
58
 
60
59
  Return a strictly valid JSON plan:
61
60
 
@@ -72,14 +71,36 @@ Return a strictly valid JSON plan:
72
71
  ? genOutput.data
73
72
  : JSON.stringify(genOutput.data ?? '{}');
74
73
  const cleaned = await cleanupModule.run({ query: intentText, content: raw });
75
- const jsonString = typeof cleaned.content === 'string'
76
- ? cleaned.content
77
- : JSON.stringify(cleaned.content ?? '{}');
78
- let plan = JSON.parse(jsonString);
79
- if (!plan || !Array.isArray(plan.steps))
74
+ let plan = null;
75
+ // --- Parse strategy ---
76
+ if (cleaned.data && typeof cleaned.data === 'object') {
77
+ console.log('[transformPlanGen][debug] Using parsed JSON from cleanupModule');
78
+ plan = cleaned.data;
79
+ }
80
+ else if (typeof cleaned.data === 'string') {
81
+ console.log('[transformPlanGen][debug] Attempting JSON extraction fallback');
82
+ const match = cleaned.data.match(/\{[\s\S]*\}/);
83
+ if (match) {
84
+ try {
85
+ plan = JSON.parse(match[0]);
86
+ console.log('[transformPlanGen][debug] Fallback JSON.parse succeeded');
87
+ }
88
+ catch (err) {
89
+ console.error('[transformPlanGen][debug] Fallback JSON.parse FAILED', err);
90
+ }
91
+ }
92
+ else {
93
+ console.warn('[transformPlanGen][debug] No JSON object found in string');
94
+ }
95
+ }
96
+ // --- Final validation (single source of truth) ---
97
+ if (!plan || !Array.isArray(plan.steps)) {
98
+ console.error('[transformPlanGen][debug] Invalid or missing plan.steps', plan);
80
99
  throw new Error('Invalid transform plan structure');
81
- if (plan.steps.length > MAX_STEPS)
100
+ }
101
+ if (plan.steps.length > MAX_STEPS) {
82
102
  plan.steps = plan.steps.slice(0, MAX_STEPS);
103
+ }
83
104
  // Map groups & metadata
84
105
  plan.steps = plan.steps.map(step => {
85
106
  const actionDef = PLAN_ACTIONS.find(a => a.action === step.action);
package/dist/db/schema.js CHANGED
@@ -25,10 +25,34 @@ export function initSchema() {
25
25
  content='files',
26
26
  content_rowid='id'
27
27
  );
28
+ `);
29
+ // --- Tasks table for collaborative workflows ---
30
+ db.exec(`
31
+ CREATE TABLE IF NOT EXISTS tasks (
32
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
33
+
34
+ -- identity & lifecycle
35
+ status TEXT NOT NULL DEFAULT 'active', -- active | paused | completed | abandoned
36
+
37
+ -- human-facing
38
+ initial_query TEXT NOT NULL, -- raw user query
39
+ summary TEXT, -- evolving short description
40
+
41
+ -- resolved collaboration state
42
+ agreed_intent TEXT,
43
+ constraints_json TEXT, -- JSON array of constraints
44
+ file_scope_json TEXT, -- JSON array of file paths
45
+
46
+ -- bookkeeping
47
+ created_at TEXT NOT NULL,
48
+ updated_at TEXT NOT NULL
49
+ );
50
+
51
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
28
52
  `);
29
53
  // --- Folder capsules ---
30
54
  db.exec(`
31
- CREATE TABLE IF NOT EXISTS folder_capsules (
55
+ CREATE TABLE IF NOT EXISTS folder_capsules(
32
56
  id INTEGER PRIMARY KEY AUTOINCREMENT,
33
57
  path TEXT UNIQUE NOT NULL,
34
58
  depth INTEGER NOT NULL,
@@ -43,79 +67,79 @@ export function initSchema() {
43
67
 
44
68
  CREATE INDEX IF NOT EXISTS idx_folder_capsules_depth
45
69
  ON folder_capsules(depth);
46
- `);
70
+ `);
47
71
  // --- Functions table ---
48
72
  db.exec(`
49
- CREATE TABLE IF NOT EXISTS functions (
50
- id INTEGER PRIMARY KEY AUTOINCREMENT,
51
- file_id INTEGER REFERENCES files(id),
52
- name TEXT,
53
- unique_id TEXT UNIQUE, -- e.g. "buildContextualPrompt@cli/src/utils/buildContextualPrompt.ts"
73
+ CREATE TABLE IF NOT EXISTS functions(
74
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
75
+ file_id INTEGER REFERENCES files(id),
76
+ name TEXT,
77
+ unique_id TEXT UNIQUE, --e.g. "buildContextualPrompt@cli/src/utils/buildContextualPrompt.ts"
54
78
  start_line INTEGER,
55
- end_line INTEGER,
56
- content TEXT,
57
- lang TEXT
58
- );
79
+ end_line INTEGER,
80
+ content TEXT,
81
+ lang TEXT
82
+ );
59
83
 
60
84
  CREATE INDEX IF NOT EXISTS idx_functions_file_id ON functions(file_id);
61
85
  CREATE INDEX IF NOT EXISTS idx_functions_unique_id ON functions(unique_id);
62
86
  `);
63
87
  // --- Graph-specific additions ---
64
88
  db.exec(`
65
- CREATE TABLE IF NOT EXISTS graph_classes (
66
- id INTEGER PRIMARY KEY AUTOINCREMENT,
67
- file_id INTEGER REFERENCES files(id),
68
- name TEXT,
69
- unique_id TEXT UNIQUE,
70
- start_line INTEGER,
71
- end_line INTEGER,
72
- content TEXT,
73
- lang TEXT
74
- );
89
+ CREATE TABLE IF NOT EXISTS graph_classes(
90
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
91
+ file_id INTEGER REFERENCES files(id),
92
+ name TEXT,
93
+ unique_id TEXT UNIQUE,
94
+ start_line INTEGER,
95
+ end_line INTEGER,
96
+ content TEXT,
97
+ lang TEXT
98
+ );
75
99
 
76
100
  CREATE INDEX IF NOT EXISTS idx_graph_classes_file_id ON graph_classes(file_id);
77
101
  CREATE INDEX IF NOT EXISTS idx_graph_classes_unique_id ON graph_classes(unique_id);
78
102
  `);
79
103
  db.exec(`
80
- CREATE TABLE IF NOT EXISTS graph_edges (
81
- id INTEGER PRIMARY KEY AUTOINCREMENT,
82
- source_type TEXT NOT NULL,
83
- source_unique_id TEXT NOT NULL,
84
- target_type TEXT NOT NULL,
85
- target_unique_id TEXT NOT NULL,
86
- relation TEXT NOT NULL
87
- );
104
+ CREATE TABLE IF NOT EXISTS graph_edges(
105
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
106
+ source_type TEXT NOT NULL,
107
+ source_unique_id TEXT NOT NULL,
108
+ target_type TEXT NOT NULL,
109
+ target_unique_id TEXT NOT NULL,
110
+ relation TEXT NOT NULL
111
+ );
88
112
 
89
113
  CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges(source_type, source_unique_id);
90
114
  CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges(target_type, target_unique_id);
91
115
  CREATE INDEX IF NOT EXISTS idx_graph_edges_relation ON graph_edges(relation);
92
116
  `);
93
117
  db.exec(`
94
- CREATE TABLE IF NOT EXISTS graph_tags_master (
95
- id INTEGER PRIMARY KEY AUTOINCREMENT,
96
- name TEXT UNIQUE NOT NULL
97
- );
118
+ CREATE TABLE IF NOT EXISTS graph_tags_master(
119
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
120
+ name TEXT UNIQUE NOT NULL
121
+ );
98
122
 
99
- CREATE TABLE IF NOT EXISTS graph_entity_tags (
100
- id INTEGER PRIMARY KEY AUTOINCREMENT,
101
- entity_type TEXT NOT NULL,
102
- entity_unique_id TEXT NOT NULL,
103
- tag_id INTEGER NOT NULL REFERENCES graph_tags_master(id),
104
- UNIQUE(entity_type, entity_unique_id, tag_id)
105
- );
123
+ CREATE TABLE IF NOT EXISTS graph_entity_tags(
124
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
125
+ entity_type TEXT NOT NULL,
126
+ entity_unique_id TEXT NOT NULL,
127
+ tag_id INTEGER NOT NULL REFERENCES graph_tags_master(id),
128
+ UNIQUE(entity_type, entity_unique_id, tag_id)
129
+ );
106
130
 
107
131
  CREATE INDEX IF NOT EXISTS idx_graph_entity_tags_entity ON graph_entity_tags(entity_type, entity_unique_id);
108
132
  CREATE INDEX IF NOT EXISTS idx_graph_entity_tags_tag ON graph_entity_tags(tag_id);
109
133
  `);
110
134
  db.exec(`
111
- CREATE TABLE IF NOT EXISTS summaries (
112
- id INTEGER PRIMARY KEY AUTOINCREMENT,
113
- path TEXT UNIQUE,
114
- type TEXT NOT NULL CHECK (type IN ('folder','project')),
115
- summary TEXT,
116
- last_generated TEXT,
117
- child_latest_modified TEXT
118
- );
135
+ CREATE TABLE IF NOT EXISTS summaries(
136
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
137
+ path TEXT UNIQUE,
138
+ type TEXT NOT NULL CHECK(type IN('folder', 'project')),
139
+ summary TEXT,
140
+ last_generated TEXT,
141
+ child_latest_modified TEXT
142
+ );
119
143
 
120
144
  CREATE INDEX IF NOT EXISTS idx_summaries_type ON summaries(type);
121
145
  CREATE INDEX IF NOT EXISTS idx_summaries_path ON summaries(path);
@@ -1,84 +1,55 @@
1
1
  // File: src/modules/cleanupModule.ts
2
2
  import chalk from "chalk";
3
- /** --- Helper: detect noise at top/bottom of content --- */
3
+ /** --- Helper: detect top/bottom fluff/noise --- */
4
4
  function isTopOrBottomNoise(line) {
5
5
  const trimmed = line.trim();
6
+ if (!trimmed)
7
+ return true;
6
8
  if (/^```(?:\w+)?$/.test(trimmed))
7
9
  return true;
8
10
  if (/^<!--.*-->$/.test(trimmed))
9
11
  return true;
10
12
  const lower = trimmed.toLowerCase();
11
- if (!trimmed.startsWith("//") && !trimmed.startsWith("/*")) {
12
- return [
13
- /^i\s/i,
14
- /^here/,
15
- /^this/,
16
- /^the following/,
17
- /^below/,
18
- /^in this/,
19
- /^we have/,
20
- /the code above/,
21
- /ensures that/,
22
- /it handles/,
23
- /used to/,
24
- /note that/,
25
- /example/,
26
- /summary/,
27
- /added comments/,
28
- ].some((pattern) => pattern.test(lower));
29
- }
30
- return false;
31
- }
32
- /** Extract first object slice { ... } */
33
- function extractObject(text) {
34
- const start = text.indexOf("{");
35
- const end = text.lastIndexOf("}");
36
- return start !== -1 && end !== -1 && end > start ? text.slice(start, end + 1) : null;
13
+ return [
14
+ /^i\s/i,
15
+ /^here/iu,
16
+ /^this/iu,
17
+ /^the following/iu,
18
+ /^below/iu,
19
+ /^in this/iu,
20
+ /^we have/iu,
21
+ /the code above/iu,
22
+ /ensures that/iu,
23
+ /it handles/iu,
24
+ /used to/iu,
25
+ /note that/iu,
26
+ /example/iu,
27
+ /summary/iu,
28
+ /added comments/iu,
29
+ ].some((pattern) => pattern.test(lower));
37
30
  }
38
- /** Extract first array slice [ ... ] */
39
- function extractArray(text) {
40
- const start = text.indexOf("[");
41
- const end = text.lastIndexOf("]");
42
- return start !== -1 && end !== -1 && end > start ? text.slice(start, end + 1) : null;
31
+ /** --- Extract first JSON object or array slice --- */
32
+ function extractJsonSlice(text) {
33
+ const objStart = text.indexOf("{");
34
+ const objEnd = text.lastIndexOf("}");
35
+ if (objStart !== -1 && objEnd > objStart)
36
+ return text.slice(objStart, objEnd + 1);
37
+ const arrStart = text.indexOf("[");
38
+ const arrEnd = text.lastIndexOf("]");
39
+ if (arrStart !== -1 && arrEnd > arrStart)
40
+ return text.slice(arrStart, arrEnd + 1);
41
+ return null;
43
42
  }
44
- /** Try parsing with fallback slice logic */
43
+ /** --- Try parsing JSON with multiple fallbacks --- */
45
44
  function parseJsonWithFallback(content) {
46
- const trimmed = content.trim();
47
- // --- 1) JSON Array strategy ---
48
- if (trimmed.startsWith("[")) {
49
- try {
50
- return JSON.parse(trimmed);
51
- }
52
- catch {
53
- const slice = extractArray(trimmed);
54
- if (slice) {
55
- try {
56
- return JSON.parse(slice);
57
- }
58
- catch { }
59
- }
60
- }
61
- }
62
- // --- 2) JSON Object strategy ---
63
- if (trimmed.startsWith("{")) {
64
- try {
65
- return JSON.parse(trimmed);
66
- }
67
- catch {
68
- const slice = extractObject(trimmed);
69
- if (slice) {
70
- try {
71
- return JSON.parse(slice);
72
- }
73
- catch { }
74
- }
75
- }
45
+ try {
46
+ return JSON.parse(content);
76
47
  }
77
- // --- 3) Fallback: if mixed text, extract first object ---
78
- const fallbackObj = extractObject(content);
79
- if (fallbackObj) {
48
+ catch { }
49
+ const slice = extractJsonSlice(content);
50
+ if (slice) {
80
51
  try {
81
- return JSON.parse(fallbackObj);
52
+ return JSON.parse(slice);
82
53
  }
83
54
  catch { }
84
55
  }
@@ -87,7 +58,7 @@ function parseJsonWithFallback(content) {
87
58
  /** --- Module --- */
88
59
  export const cleanupModule = {
89
60
  name: "cleanup",
90
- description: "Removes markdown fences, fluff, and reasoning text; extracts valid JSON if possible.",
61
+ description: "Cleans up AI output, removes noise, and extracts valid JSON if possible.",
91
62
  groups: ["transform"],
92
63
  async run(input) {
93
64
  // --- Normalize input ---
@@ -97,39 +68,30 @@ export const cleanupModule = {
97
68
  content = content.replace(/\r\n/g, "\n");
98
69
  // --- Trim top/bottom noise ---
99
70
  let lines = content.split("\n");
100
- while (lines.length && (lines[0].trim() === "" || isTopOrBottomNoise(lines[0])))
71
+ while (lines.length && isTopOrBottomNoise(lines[0]))
101
72
  lines.shift();
102
- while (lines.length && (lines[lines.length - 1].trim() === "" || isTopOrBottomNoise(lines[lines.length - 1]))) {
73
+ while (lines.length && isTopOrBottomNoise(lines[lines.length - 1]))
103
74
  lines.pop();
104
- }
105
- content = lines.join("\n");
106
- // --- Strip markdown fences, comments, thinking tags ---
75
+ content = lines.join("\n").trim();
76
+ // --- Remove fences, HTML comments, thinking tags ---
107
77
  content = content
108
78
  .replace(/```(?:json)?/gi, "")
109
79
  .replace(/```/g, "")
110
80
  .replace(/<!--.*?-->/gs, "")
111
81
  .replace(/<think>[\s\S]*?<\/think>/gi, "")
112
82
  .trim();
113
- // If no JSON markers at all → treat as plain cleaned text
114
- if (!content.includes("{") && !content.includes("[")) {
115
- return { query: input.query, data: content };
83
+ // --- Attempt parsing ---
84
+ let parsed = null;
85
+ if (content.includes("{") || content.includes("[")) {
86
+ parsed = parseJsonWithFallback(content);
116
87
  }
117
- // --- Parse JSON using simplified strategy ---
118
- const parsed = parseJsonWithFallback(content);
119
88
  if (parsed !== null) {
120
- return {
121
- query: input.query,
122
- content,
123
- data: parsed,
124
- };
89
+ return { query: input.query, content, data: parsed };
125
90
  }
126
- // Clear current line (removes leftover spinner)
127
- process.stdout.write('\r\x1b[K');
128
- console.warn(chalk.red(" - [cleanupModule] Failed to parse JSON — returning raw content."));
129
- return {
130
- query: input.query,
131
- content,
132
- data: content,
133
- };
91
+ // --- Fallback: return raw cleaned text ---
92
+ // Use console.debug instead of warn so logs are quieter
93
+ process.stdout.write("\r\x1b[K");
94
+ console.debug(chalk.yellow(` - [cleanupModule] Could not parse JSON, returning raw content.`));
95
+ return { query: input.query, content, data: content };
134
96
  },
135
97
  };
@@ -19,6 +19,34 @@ if (!fs.existsSync(dbPath)) {
19
19
  process.exit(1);
20
20
  }
21
21
  const db = new Database(dbPath);
22
+ /* ───────────────────────── tasks ───────────────────────── */
23
+ header("📝 tasks");
24
+ // check if table exists
25
+ const tasksExists = db
26
+ .prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='tasks'`)
27
+ .get();
28
+ if (!tasksExists) {
29
+ console.log("❌ tasks table missing — you need to run initSchema with tasks");
30
+ }
31
+ else {
32
+ const totalTasks = tableCount("tasks");
33
+ const tasksWithQuery = nonEmptyCount("tasks", "initial_query");
34
+ console.log(`📊 total tasks: ${totalTasks}`);
35
+ console.log(`✏️ tasks with initial_query: ${tasksWithQuery}`);
36
+ const sampleTasks = db.prepare(`
37
+ SELECT id, initial_query, status, created_at
38
+ FROM tasks
39
+ ORDER BY created_at DESC
40
+ LIMIT 3
41
+ `).all();
42
+ if (sampleTasks.length === 0) {
43
+ console.log("⚠️ no tasks yet");
44
+ }
45
+ else {
46
+ console.log("✅ sample tasks:");
47
+ sampleTasks.forEach(t => console.log(` [${t.id}] "${t.initial_query}" (status=${t.status}, created=${t.created_at})`));
48
+ }
49
+ }
22
50
  /* ───────────────────────── helpers ───────────────────────── */
23
51
  function tableCount(table) {
24
52
  return db.prepare(`SELECT COUNT(*) AS c FROM ${table}`).get().c;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.126",
3
+ "version": "0.1.127",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"