@semalt-ai/code 1.8.3 → 1.8.5

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.
@@ -15,7 +15,9 @@
15
15
  "Bash(python3 *)",
16
16
  "Read(//tmp/**)",
17
17
  "Bash(sed -i \"s/addMessage\\('>>> AI MSG 2'.*$/addMessage\\('>>> AI MSG 2', ['response body 2a', 'response body 2b']\\);\\\\nfor \\(let k = 3; k <= 8; k++\\) { addMessage\\('>>> USER MSG ' + k, ['line body ' + k]\\); addMessage\\('>>> AI MSG ' + k, ['response body ' + k + 'a', 'response body ' + k + 'b']\\); }/\" scroll-capture.js)",
18
- "Bash(echo \"exit=$?\")"
18
+ "Bash(echo \"exit=$?\")",
19
+ "Bash(echo \"---grep done, exit=$?---\")",
20
+ "Bash(grep *)"
19
21
  ]
20
22
  }
21
23
  }
package/CLAUDE.md CHANGED
@@ -87,7 +87,10 @@ semalt-code config [set <key> <val>] # show or update config keys
87
87
  --dashboard-url <url> dashboard base URL (overrides config)
88
88
  --default-model <name> set default model in config
89
89
  --show-think display model reasoning (thinking) content
90
- --debug print raw AI response (stderr) each iteration
90
+ --debug inline debug: per-iteration debug block in chat history (TUI-safe)
91
+ --debug-file <path> extended debug: per-iteration block + raw SSE chunks
92
+ + request body dumps written to <path>, nothing to stdout.
93
+ Mutually exclusive with --debug.
91
94
  --allow-fs auto-approve all filesystem operations
92
95
  --allow-exec auto-approve shell command execution
93
96
  --allow-net auto-approve network operations
@@ -0,0 +1,66 @@
1
+ ## Activity region in-place update breaks when a modal is open
2
+
3
+ When a modal occupies screen lines below an active activity bubble, the
4
+ activity region's redraw mechanism appears to fall back to scrollback
5
+ append per tick instead of in-place rewrite. Surfaced via the `ask_user`
6
+ ticking-timer bug; mitigated by making `ask_user` a static bubble.
7
+
8
+ Latent: any future long-running tool that opens a modal concurrently
9
+ (none today) will reproduce the fragmentation. Fix likely involves
10
+ making the activity region modal-aware in `lib/ui/writer.js` — when a
11
+ modal region is active, route activity updates through a path that
12
+ clears modal, redraws activity, redraws modal — or reserves activity
13
+ above the modal in a way that survives modal lifecycle.
14
+
15
+ Not blocking. Revisit if a second use-case appears.
16
+
17
+ ## `cmdShell` and `chatStream` write to stdout bypassing the writer
18
+
19
+ Several call sites currently emit directly to `process.stdout.write`
20
+ without going through `lib/ui/writer.js`:
21
+
22
+ - `lib/commands.js` (`cmdShell`)
23
+ - `lib/api.js` (streaming output path)
24
+
25
+ These were flagged during the Phase 2 writer audit and annotated as
26
+ `// audit: allowed` because they need to interleave with synchronous
27
+ writes from `StreamRenderer`. Routing them through the writer today
28
+ would require buffering or sequencing changes that don't compose with
29
+ how `StreamRenderer` flushes content per chunk.
30
+
31
+ Resolves when: `StreamRenderer` itself is migrated to write through
32
+ the writer. After that, the bypass annotations can be removed and
33
+ these call sites become normal `writer.scrollback(...)` calls.
34
+
35
+ Not blocking. The audit annotation makes the bypass intentional and
36
+ greppable. Revisit when `StreamRenderer` migration is on the table.
37
+
38
+ ## Tool result storage: single `content` field used for both model and UI
39
+
40
+ Storage (PHP backend, MySQL `messages` table) holds one `content` field
41
+ per tool result. The full payload is required for the model on
42
+ subsequent turns, but the UI needs a compact summary (e.g. `net · GET
43
+ https://... · 200 · 256 KB`).
44
+
45
+ Today this is handled UI-side: `summarizeToolResult` in
46
+ `lib/ui/format.js` runs read-side heuristics on the raw `content` every
47
+ time `/history` renders. Heuristics cover HTTP, exec, file ops, with
48
+ a fallback for unknown shapes. They work in practice but are a
49
+ compromise — any tool whose output format drifts will fall through to
50
+ the generic fallback until the heuristic is updated.
51
+
52
+ Full fix: storage holds both `content` (full, model-bound) and
53
+ `display` (pre-rendered summary, UI-bound). Summary is generated
54
+ write-side at tool execution time, when the live activity bubble
55
+ already produces the right string — that string just needs to be
56
+ captured and persisted alongside the full content.
57
+
58
+ Resolves when: backend schema migration for native function calling
59
+ lands (Phase 2.2 of the native-tools plan, which already touches the
60
+ `messages` table). Adding a `display` column in the same migration is
61
+ cheap; doing it as a separate migration later is not. When this lands,
62
+ `summarizeToolResult` becomes unnecessary for new tool results; it
63
+ stays only as a fallback for legacy rows lacking `display`.
64
+
65
+ Not blocking — current heuristics cover all 33 tools' output shapes.
66
+ Track until Phase 2.2 lands.
package/index.js CHANGED
@@ -18,6 +18,7 @@ const { createCommands } = require('./lib/commands');
18
18
  const { parseArgs } = require('./lib/args');
19
19
  const { CONFIG_PATH } = require('./lib/constants');
20
20
  const { AUDIT_LOG } = require('./lib/audit');
21
+ const writer = require('./lib/ui/writer');
21
22
 
22
23
  // Install process-wide signal handlers so every exit path (normal, SIGINT,
23
24
  // SIGHUP, SIGTERM, uncaught exception) restores the terminal. Safe to call
@@ -48,7 +49,7 @@ if (_argv.includes('--allow-all')) {
48
49
  const _readonly = _argv.includes('--readonly');
49
50
 
50
51
  const permissionManager = createPermissionManager(ui, { allowedTiers: _allowedTiers, readonly: _readonly });
51
- const { agentExecShell, agentExecFile } = createToolExecutor(permissionManager, ui, getConfig);
52
+ const { agentExecShell, agentExecFile, describePermission } = createToolExecutor(permissionManager, ui, getConfig);
52
53
  const apiClient = createApiClient({
53
54
  getConfig,
54
55
  saveConfig: (nextConfig) => {
@@ -65,6 +66,8 @@ const { runAgentLoop } = createAgentRunner({
65
66
  }),
66
67
  agentExecShell,
67
68
  agentExecFile,
69
+ describePermission,
70
+ permissionManager,
68
71
  ui,
69
72
  getConfig,
70
73
  });
@@ -90,7 +93,7 @@ async function main() {
90
93
  const command = rawArgs[0];
91
94
 
92
95
  if (command === '--help' || command === '-h') {
93
- console.log(`
96
+ writer.scrollback(`
94
97
  Semalt.AI — Self-hosted AI Coding Assistant
95
98
 
96
99
  Usage: semalt-code [command] [options]
@@ -118,7 +121,12 @@ Options:
118
121
  --dashboard-url <url> Dashboard URL (init)
119
122
  --default-model <name> Default model (init)
120
123
  --show-think Display model reasoning (thinking) content
121
- --debug Print messages sent to agent + raw AI response (stderr) each iteration
124
+ --debug Inline debug output: per-iteration debug block in the
125
+ chat history. TUI-safe; no per-chunk noise.
126
+ --debug-file <path> Extended debug to file: per-iteration block PLUS raw
127
+ SSE chunks, request body dumps, accumulator state,
128
+ and other high-volume traces. Nothing prints to stdout
129
+ — the TUI stays clean. Mutually exclusive with --debug.
122
130
  --allow-fs Auto-approve all filesystem operations
123
131
  --allow-exec Auto-approve shell command execution
124
132
  --allow-net Auto-approve network operations
@@ -130,11 +138,13 @@ Options:
130
138
 
131
139
  Config: ${CONFIG_PATH}
132
140
  `);
141
+ await writer.flush();
133
142
  return;
134
143
  }
135
144
 
136
145
  if (command === '--version' || command === '-v') {
137
- console.log(PACKAGE_JSON.version);
146
+ writer.scrollback(PACKAGE_JSON.version);
147
+ await writer.flush();
138
148
  return;
139
149
  }
140
150
 
@@ -170,20 +180,22 @@ Config: ${CONFIG_PATH}
170
180
  try {
171
181
  const entry = JSON.parse(line);
172
182
  const icon = entry.approved ? `${ui.FG_GREEN}✓${ui.RST}` : `${ui.FG_RED}✗${ui.RST}`;
173
- console.log(`${icon} ${line}`);
183
+ writer.scrollback(`${icon} ${line}`);
174
184
  } catch {
175
- console.log(line);
185
+ writer.scrollback(line);
176
186
  }
177
187
  }
178
188
  } catch {
179
- console.log('No audit log found.');
189
+ writer.scrollback('No audit log found.');
180
190
  }
191
+ await writer.flush();
181
192
  } else if (command === 'config') {
182
193
  const sub = rawArgs[1];
183
194
  if (sub === 'set') {
184
195
  const key = rawArgs[2];
185
196
  const value = rawArgs[3];
186
197
  if (!key || value === undefined) {
198
+ // audit: allowed — pre-UI argparse usage error to stderr; exits immediately.
187
199
  process.stderr.write(`Usage: semalt-code config set <key> <value>\n`);
188
200
  process.exit(1);
189
201
  }
@@ -194,11 +206,12 @@ Config: ${CONFIG_PATH}
194
206
  parsed = value;
195
207
  }
196
208
  configSet(key, parsed);
197
- console.log(`Set ${key} = ${JSON.stringify(parsed)}`);
209
+ writer.scrollback(`Set ${key} = ${JSON.stringify(parsed)}`);
198
210
  } else {
199
211
  // default: "show" or bare "config"
200
- console.log(configShow());
212
+ writer.scrollback(configShow());
201
213
  }
214
+ await writer.flush();
202
215
  } else {
203
216
  const { opts } = parseArgs(rawArgs);
204
217
  await commands.cmdChat(opts);
@@ -209,6 +222,7 @@ main().catch((error) => {
209
222
  // Tear down the TUI synchronously so the error message lands below the
210
223
  // last scrollback line, not on top of a still-rendered live region.
211
224
  try { ui.teardownTerminal(); } catch {}
225
+ // audit: allowed — fatal error message to stderr after writer teardown.
212
226
  process.stderr.write(`\n ${ui.FG_RED}✗ Fatal: ${error.message}${ui.RST}\n\n`);
213
227
  process.exit(1);
214
228
  });