pi-supernova 0.9.0 → 0.10.0

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,16 +1,13 @@
1
1
  // Standing tool description: sent on every request. No result or history compression.
2
- export const REFERENCE = `JS body/async arrow: read/write/edit/bash; no fs/import/require. file: workspace scripts; data: literals (≤48000 JSON chars).
3
- read(path|paths,offset=1,limit?) → raw text/text[]; directories → entries; images: PNG/JPEG/GIF/WebP (≤16 images/20 MiB).
4
- Text ≤64 MiB internally; complete:true requires the whole file. Display alone is capped; return a summary or read(path,{offset:1,limit:80}). Larger files/JSONL: bounded bash parser.
5
- read({path,json:selector}) → parsed JSON: ".field", ".a[0:3]", ".a.length", quoted keys, true; input ≤16 MiB, selections ≤64 MiB storage, no jq. Values retain their types.
6
- read("symbol or question") = read({query,resolve:true}) → view; check status; view.text is a span, not the file.
7
- read(path,{about}) → windows; read({query,evidence:true}) → ranked evidence; read({path,outline:true}) → declarations.
8
- write(path,text) replaces unread workspace files; write({path,content,append:true}) appends without reading. After read: edit or replace:true.
9
- edit(path,oldText,newText) | edit({path,edits:[{oldText,newText}]}) unique exact read text; returns numbered windows/checks/references.
10
- edit(view,text) replaces span; edit(view,old,new) uniquely matches within it. edit(async()=>{...}) checkpoint: merge on success, rollback/rethrow on failure.
11
- bash(command,{cwd?,timeoutMs?}) | bash({command,args}) literal argv for scripts; bounded output, nonzero throws. Outer timeoutMs caps ALL waits/commands; bash inherits unless overridden.
12
- Edits commit on success/before bash. Promise.allSettled keeps partial results.
13
- programs:[{code?,file?,data?}] inherits source/data; entries override. mergeData:true shallow object merge (entry keys win).
14
- Promise.all: ≤8 reads or disjoint-file mutations; same-file serial; bash/checkpoint barriers.
15
- Fresh guests/separate commits. Sequential failure stops; prior commits stay. parallel:true for disjoint entries. ONE call for known work; split for new decisions.
2
+ export const REFERENCE = `JS body/async arrow; read/write/edit/bash, no fs/import/require. file: workspace JS; data: literals ≤48000 JSON chars.
3
+ read(path|paths,offset=1,limit?) → text/text[]; dirs→entries; PNG/JPEG/GIF/WebP ≤16/20MiB.
4
+ Text ≤64 MiB internally; complete:true requires whole file. Display capped: summarize or read(path,{offset:1,limit:80}); larger files via bash.
5
+ read({path,json:true|selector}) → JSON, no jq (16MiB input/64MiB selection). Values retain their types.
6
+ read("symbol or question") = read({query,resolve:true}) → view; check status; view.text is a span. read(path,{about}) windows; read({query,evidence:true}) evidence; read({path,outline:true}) declarations.
7
+ write/edit workspace-only; external changes need separately authorized command. write(path,text) or {path,content,append:true}; after read edit or replace:true.
8
+ edit(path,oldText,newText) or {path,edits:[{oldText,newText}]}; unique exact match; numbered windows. edit(view,text) or edit(view,old,new). edit(async()=>{...}) checkpoint: no bash; merge/rollback+rethrow.
9
+ bash(command,{cwd?,timeoutMs?}) or {command,args}; nonzero throws. Outer timeout bounds foreground; bash inherits. Shell mutations flush edits.
10
+ bash({command,background:true,pty?:true,timeoutMs?:1800000})→sessionId. bash({action:"list"}) or {sessionId,action:"poll"|"write"|"stop",cursor?,waitMs?,input?}→status/output/cursor/exitCode. PTY macOS/Linux; jobs end at shutdown.
11
+ programs:[{code?,file?,data?}] inherit defaults; mergeData:true shallow. Fresh guests/commits; sequential failure stops, prior commits stay; parallel:true disjoint work.
12
+ Promise.all ≤8 disjoint reads/mutations; same-file serial; bash/checkpoints barriers. Optional reads: Promise.allSettled retains successes. ONE call for known work.
16
13
  `;
@@ -1,12 +1,26 @@
1
+ import stringWidth from "string-width";
2
+
1
3
  /** Bounded source context for parse and syntax diagnostics. */
4
+ const JS_LINES = /\r\n|[\n\r\u2028\u2029]/;
5
+
6
+ const JSON_LINES = /\r\n|[\n\r]/;
7
+
8
+ // Source columns are UTF-16 offsets; expand controls identically before the
9
+ // source token and the caret. JSON strings can contain literal Unicode LS/PS.
10
+ const displaySource = text => text.replaceAll("\t"," ").replaceAll("\u2028","\\u2028").replaceAll("\u2029","\\u2029");
2
11
 
3
- export function sourceContext(source, line, column) {
12
+ export function sourceContext(source, line, column, lineBreaks = JS_LINES) {
4
13
  if (!Number.isInteger(line) || line < 1) return "";
5
- const text = String(source).split("\n")[line - 1];
14
+ const text = String(source).split(lineBreaks)[line - 1];
6
15
 
7
16
  if (text === undefined) return "";
8
- const shown = text.length > 160 ? text.slice(0, 160) : text;
9
- const caret = Number.isInteger(column) ? " ".repeat(Math.min(column, shown.length)) + "^" : "";
17
+ const located = Number.isInteger(column) && column >= 0;
18
+ const position = located ? Math.min(column, text.length) : 0;
19
+ const start = Math.max(0, Math.min(position - 80, text.length - 160));
20
+ const end = Math.min(text.length, start + 160);
21
+ const prefix = start > 0 ? "…" : "";
22
+ const shown = prefix + displaySource(text.slice(start, end)) + (end < text.length ? "…" : "");
23
+ const caret = located ? " ".repeat(stringWidth(prefix + displaySource(text.slice(start, position)))) + "^" : "";
10
24
 
11
25
  return "\n " + shown + (caret ? "\n " + caret : "");
12
26
  }
@@ -24,11 +38,12 @@ export function parsePosition(message, source) {
24
38
  const offsetMatch = /at position (\d+)/.exec(String(message));
25
39
 
26
40
  if (!offsetMatch) return null;
41
+
27
42
  return offsetPosition(source, Number(offsetMatch[1]));
28
43
  }
29
44
 
30
45
  function offsetPosition(source, offset) {
31
- const lines = String(source).slice(0, offset).split("\n");
46
+ const lines = String(source).slice(0, offset).split(JSON_LINES);
32
47
 
33
48
  return { line: lines.length, column: lines.at(-1).length };
34
49
  }
@@ -37,6 +52,7 @@ function offsetPosition(source, offset) {
37
52
  // offsets. JSON.parse remains the authority; cap extra work at 65,536 characters.
38
53
  // eslint-disable-next-line no-control-regex -- RFC 8259 excludes unescaped control characters from strings.
39
54
  const JSON_TOKEN = /("(?:[^"\\\u0000-\u001f]|\\(?:["\\/bfnrt]|u[\da-fA-F]{4}))*")|(-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?)|(true|false|null)|[{}[\]:,]/y;
55
+
40
56
  const JSON_NEXT = {
41
57
  end: { eof: [] },
42
58
  value: { string: [], number: [], literal: [], "{": ["object"], "[": ["array"] },
@@ -67,9 +83,12 @@ function invalidJsonOffset(source) {
67
83
  while (stack.length) {
68
84
  const token = jsonTokenAt(source, offset);
69
85
  let state = stack.pop();
86
+
70
87
  if (state === "array" && token.kind !== "]") { stack.push("arrayNext"); state = "value"; }
88
+
71
89
  if (state === "object" && token.kind !== "}") state = "key";
72
90
  const next = JSON_NEXT[state][token.kind];
91
+
73
92
  if (!next) return token.start;
74
93
  stack.push(...next);
75
94
  offset = token.end;
@@ -80,10 +99,12 @@ function invalidJsonOffset(source) {
80
99
 
81
100
  export function jsonErrorContext(message, source) {
82
101
  const native = parsePosition(message, source);
83
- if (native) return sourceContext(source, native.line, native.column);
102
+
103
+ if (native) return sourceContext(source, native.line, native.column, JSON_LINES);
84
104
  const offset = invalidJsonOffset(source);
105
+
85
106
  if (offset === null) return "";
86
107
  const { line, column } = offsetPosition(source, offset);
87
108
 
88
- return " (near line " + line + " column " + (column + 1) + ")" + sourceContext(source, line, column);
109
+ return " (near line " + line + " column " + (column + 1) + ")" + sourceContext(source, line, column, JSON_LINES);
89
110
  }