picocode-core 0.9.120 → 0.9.122

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picocode-core",
3
- "version": "0.9.120",
3
+ "version": "0.9.122",
4
4
  "description": "The agent runtime behind pico: sessions, tools, subagents, MCP, memory, and model access",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/ansi.js ADDED
@@ -0,0 +1,52 @@
1
+ const CSI_RE = /\x1b\[[0-9;?]*[ -/]*[@-~]/g
2
+ const MAX_ACTIVE_SGR = 32
3
+
4
+ export function stripAnsi(text) {
5
+ return String(text).replace(CSI_RE, '')
6
+ }
7
+
8
+ function isReset(params) {
9
+ return params === '' || params === '0' || params.split(';').every((code) => code === '0' || code === '')
10
+ }
11
+
12
+ // remembers which sgr sequences are still in effect so a line that starts
13
+ // mid-style can be prefixed with exactly the sequences the terminal would
14
+ // have applied, without interpreting colors at all
15
+ export function createSgrTracker() {
16
+ let active = []
17
+ let pending = ''
18
+
19
+ function feed(text) {
20
+ const input = pending + String(text)
21
+ pending = ''
22
+ let i = 0
23
+ while (i < input.length) {
24
+ const start = input.indexOf('\x1b[', i)
25
+ if (start === -1) break
26
+ let end = start + 2
27
+ while (end < input.length && !(input.charCodeAt(end) >= 0x40 && input.charCodeAt(end) <= 0x7e)) end++
28
+ if (end >= input.length) {
29
+ pending = input.slice(start)
30
+ break
31
+ }
32
+ if (input[end] === 'm') apply(input.slice(start + 2, end))
33
+ i = end + 1
34
+ }
35
+ }
36
+
37
+ function apply(params) {
38
+ if (isReset(params)) {
39
+ active = []
40
+ return
41
+ }
42
+ if (params.startsWith('0;')) active = []
43
+ active.push(params.startsWith('0;') ? params.slice(2) : params)
44
+ if (active.length > MAX_ACTIVE_SGR) active = active.slice(-MAX_ACTIVE_SGR)
45
+ }
46
+
47
+ function style() {
48
+ return active.map((params) => `\x1b[${params}m`).join('')
49
+ }
50
+
51
+ return { feed, style }
52
+ }
package/src/controller.js CHANGED
@@ -773,14 +773,16 @@ export function createController({ boot }) {
773
773
  let value
774
774
  if (!input) {
775
775
  value = values[(values.indexOf(state.derived.color) + 1) % values.length]
776
+ } else if (input.toLowerCase() === 'none') {
777
+ value = null
776
778
  } else {
777
779
  value = SESSION_COLORS[input.toLowerCase()] || (/^#[0-9a-fA-F]{6}$/.test(input) ? input : null)
778
- if (!value) return flash(`usage: /color to cycle, or /color <${names.join('|')}|#hex>`)
780
+ if (!value) return flash(`usage: /color to cycle, /color none to clear, or /color <${names.join('|')}|#hex>`)
779
781
  }
780
782
  persist(makeEvent('color', { value }))
781
783
  ensureSession()
782
784
  reDerive()
783
- flash(`session color: ${names[values.indexOf(value)] || value}`)
785
+ flash(value ? `session color: ${names[values.indexOf(value)] || value}` : 'session color cleared')
784
786
  }
785
787
 
786
788
  function clear() {
package/src/tools/bash.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { spawn } from 'node:child_process'
2
- import { sgr, stripAnsi, updateSgrState } from '@trendr/core'
2
+ import { stripAnsi, createSgrTracker } from '../ansi.js'
3
3
 
4
4
  const MAX_OUTPUT_CHARS = 30000
5
5
  const MAX_BUFFER_BYTES = 10 * 1024 * 1024
@@ -17,19 +17,19 @@ function createOutputPreview() {
17
17
  let current = ''
18
18
  let currentStartStyle = ''
19
19
  let completed = 0
20
- const ansiState = { fg: null, bg: null, attrs: 0 }
20
+ const sgr = createSgrTracker()
21
21
 
22
22
  function add(chunk) {
23
23
  const parts = String(chunk).split('\n')
24
24
  current = (current + parts[0]).slice(0, MAX_PREVIEW_LINE_CHARS)
25
- updateSgrState(parts[0], ansiState)
25
+ sgr.feed(parts[0])
26
26
  for (let i = 1; i < parts.length; i++) {
27
27
  lines.push({ text: current, startStyle: currentStartStyle })
28
28
  if (lines.length > OUTPUT_PREVIEW_LINES) lines.shift()
29
29
  completed++
30
- currentStartStyle = ansiState.fg != null || ansiState.bg != null || ansiState.attrs ? sgr(ansiState.fg, ansiState.bg, ansiState.attrs) : ''
30
+ currentStartStyle = sgr.style()
31
31
  current = parts[i].slice(0, MAX_PREVIEW_LINE_CHARS)
32
- updateSgrState(parts[i], ansiState)
32
+ sgr.feed(parts[i])
33
33
  }
34
34
  }
35
35