@blackwell-systems/gcf 0.4.0 → 0.6.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  # gcf-typescript
8
8
 
9
- TypeScript implementation of [GCF (Graph Compact Format)](https://gcformat.com/) — the most token-efficient wire format for LLMs. A drop-in alternative to JSON and TOON for any structured data.
9
+ TypeScript implementation of [GCF](https://gcformat.com/) — the most token-efficient wire format for LLMs. A drop-in alternative to JSON and TOON for any structured data.
10
10
 
11
11
  **79% fewer input tokens than JSON. 75% fewer output tokens. 52% smaller than TOON. 100% LLM comprehension at 500 symbols, where JSON scores 76.9% and TOON scores 92.3%.**
12
12
 
@@ -44,33 +44,21 @@ Or install globally: `npm install -g @blackwell-systems/gcf` then use `gcf` dire
44
44
  ### Quick Start
45
45
 
46
46
  ```typescript
47
- import { encode, type Payload } from '@blackwell-systems/gcf';
47
+ import { encodeGeneric } from '@blackwell-systems/gcf';
48
48
 
49
- const p: Payload = {
50
- tool: 'context_for_task',
51
- tokenBudget: 5000,
52
- tokensUsed: 1847,
53
- symbols: [
54
- { qualifiedName: 'pkg.AuthMiddleware', kind: 'function', score: 0.78, provenance: 'lsp_resolved', distance: 0 },
55
- { qualifiedName: 'pkg.NewServer', kind: 'function', score: 0.54, provenance: 'lsp_resolved', distance: 1 },
56
- ],
57
- edges: [
58
- { source: 'pkg.NewServer', target: 'pkg.AuthMiddleware', edgeType: 'calls' },
49
+ const output = encodeGeneric({
50
+ employees: [
51
+ { id: 1, name: 'Alice', department: 'Engineering', salary: 95000 },
52
+ { id: 2, name: 'Bob', department: 'Sales', salary: 72000 },
59
53
  ],
60
- };
61
-
62
- const output = encode(p);
54
+ });
63
55
  ```
64
56
 
65
57
  Output:
66
58
  ```
67
- GCF tool=context_for_task budget=5000 tokens=1847 symbols=2 edges=1
68
- ## targets
69
- @0 fn pkg.AuthMiddleware 0.78 lsp_resolved
70
- ## related
71
- @1 fn pkg.NewServer 0.54 lsp_resolved
72
- ## edges [1]
73
- @0<@1 calls
59
+ ## employees [2]{id,name,department,salary}
60
+ 1|Alice|Engineering|95000
61
+ 2|Bob|Sales|72000
74
62
  ```
75
63
 
76
64
  ## Decode
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Decode any GCF text (tabular or graph profile) back into a JS value.
3
+ * Returns objects, arrays, and primitives matching the original structure.
4
+ *
5
+ * If the input starts with "GCF " (graph profile), falls back to decode()
6
+ * and returns the Payload as a plain object.
7
+ */
8
+ export declare function decodeGeneric(input: string): any;
9
+ //# sourceMappingURL=decode_generic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode_generic.d.ts","sourceRoot":"","sources":["../src/decode_generic.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAiChD"}
@@ -0,0 +1,236 @@
1
+ import { decode } from './decode.js';
2
+ /**
3
+ * Decode any GCF text (tabular or graph profile) back into a JS value.
4
+ * Returns objects, arrays, and primitives matching the original structure.
5
+ *
6
+ * If the input starts with "GCF " (graph profile), falls back to decode()
7
+ * and returns the Payload as a plain object.
8
+ */
9
+ export function decodeGeneric(input) {
10
+ input = input.trimEnd();
11
+ if (!input)
12
+ return null;
13
+ const lines = input.split('\n');
14
+ // Graph profile fallback.
15
+ if (lines[0].startsWith('GCF ')) {
16
+ const p = decode(input);
17
+ return {
18
+ tool: p.tool,
19
+ tokenBudget: p.tokenBudget,
20
+ tokensUsed: p.tokensUsed,
21
+ packRoot: p.packRoot ?? '',
22
+ symbols: p.symbols.map(s => ({
23
+ qualifiedName: s.qualifiedName,
24
+ kind: s.kind,
25
+ score: s.score,
26
+ provenance: s.provenance,
27
+ distance: s.distance,
28
+ })),
29
+ edges: p.edges.map(e => ({
30
+ source: e.source,
31
+ target: e.target,
32
+ edgeType: e.edgeType,
33
+ ...(e.status ? { status: e.status } : {}),
34
+ })),
35
+ };
36
+ }
37
+ const result = {};
38
+ parseObject(lines, 0, 0, result);
39
+ return result;
40
+ }
41
+ function parseObject(lines, start, depth, out) {
42
+ const indent = ' '.repeat(depth);
43
+ let i = start;
44
+ while (i < lines.length) {
45
+ const raw = lines[i].replace(/\r$/, '');
46
+ if (raw === '' || raw.startsWith('# ')) {
47
+ i++;
48
+ continue;
49
+ }
50
+ if (depth > 0 && !raw.startsWith(indent))
51
+ break;
52
+ const content = depth > 0 ? raw.slice(indent.length) : raw;
53
+ // Skip _summary.
54
+ if (content.startsWith('## _summary')) {
55
+ i++;
56
+ continue;
57
+ }
58
+ // Tabular array or section header.
59
+ if (content.startsWith('## ')) {
60
+ const header = content.slice(3);
61
+ const bracketIdx = header.indexOf(' [');
62
+ if (bracketIdx >= 0) {
63
+ const name = header.slice(0, bracketIdx);
64
+ const rest = header.slice(bracketIdx + 2);
65
+ const closeBracket = rest.indexOf(']');
66
+ if (closeBracket >= 0) {
67
+ const afterBracket = rest.slice(closeBracket + 1);
68
+ if (afterBracket.startsWith('{')) {
69
+ // Tabular with fields.
70
+ const fieldEnd = afterBracket.indexOf('}');
71
+ if (fieldEnd >= 0) {
72
+ const fields = afterBracket.slice(1, fieldEnd).split(',');
73
+ i++;
74
+ const [rows, consumed] = parseTabularRows(lines, i, depth, fields);
75
+ out[name] = rows;
76
+ i += consumed;
77
+ continue;
78
+ }
79
+ }
80
+ else {
81
+ const countStr = rest.slice(0, closeBracket);
82
+ if (countStr === '0') {
83
+ out[name] = [];
84
+ i++;
85
+ continue;
86
+ }
87
+ // Non-uniform array.
88
+ i++;
89
+ const [items, consumed] = parseNonUniformArray(lines, i, depth);
90
+ out[name] = items;
91
+ i += consumed;
92
+ continue;
93
+ }
94
+ }
95
+ }
96
+ // Plain section header.
97
+ let name = header;
98
+ const bi = name.indexOf(' [');
99
+ if (bi >= 0)
100
+ name = name.slice(0, bi);
101
+ i++;
102
+ const nested = {};
103
+ const consumed = parseObject(lines, i, depth + 1, nested);
104
+ out[name] = nested;
105
+ i += consumed;
106
+ continue;
107
+ }
108
+ // Inline primitive array: name[N]: val1,val2,...
109
+ const bracketIdx = content.indexOf('[');
110
+ if (bracketIdx > 0) {
111
+ const colonIdx = content.indexOf(']: ');
112
+ if (colonIdx > bracketIdx) {
113
+ const name = content.slice(0, bracketIdx);
114
+ const valsStr = content.slice(colonIdx + 3);
115
+ out[name] = valsStr.split(',').map(v => parseValue(v.trim()));
116
+ i++;
117
+ continue;
118
+ }
119
+ }
120
+ // Key=value.
121
+ const eqIdx = content.indexOf('=');
122
+ if (eqIdx > 0) {
123
+ const key = content.slice(0, eqIdx);
124
+ const val = content.slice(eqIdx + 1);
125
+ out[key] = parseValue(val);
126
+ i++;
127
+ continue;
128
+ }
129
+ i++;
130
+ }
131
+ return i - start;
132
+ }
133
+ function parseTabularRows(lines, start, depth, fields) {
134
+ const indent = ' '.repeat(depth);
135
+ const rows = [];
136
+ let i = start;
137
+ while (i < lines.length) {
138
+ const raw = lines[i].replace(/\r$/, '');
139
+ if (raw === '') {
140
+ i++;
141
+ continue;
142
+ }
143
+ const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
144
+ if (content === null)
145
+ break;
146
+ if (content.startsWith('## '))
147
+ break;
148
+ if (content.startsWith('# ')) {
149
+ i++;
150
+ continue;
151
+ }
152
+ let rowData = content;
153
+ let hasNested = false;
154
+ if (rowData.startsWith('@')) {
155
+ const sp = rowData.indexOf(' ');
156
+ if (sp > 0) {
157
+ rowData = rowData.slice(sp + 1);
158
+ hasNested = true;
159
+ }
160
+ }
161
+ const vals = rowData.split('|');
162
+ const row = {};
163
+ for (let j = 0; j < fields.length; j++) {
164
+ row[fields[j]] = j < vals.length ? parseValue(vals[j]) : null;
165
+ }
166
+ i++;
167
+ if (hasNested) {
168
+ const nestedIndent = indent + ' ';
169
+ while (i < lines.length) {
170
+ const nl = lines[i].replace(/\r$/, '');
171
+ if (!nl.startsWith(nestedIndent))
172
+ break;
173
+ const nc = nl.slice(nestedIndent.length);
174
+ if (nc.startsWith('.')) {
175
+ const fieldName = nc.slice(1);
176
+ i++;
177
+ const nested = {};
178
+ const consumed = parseObject(lines, i, depth + 2, nested);
179
+ row[fieldName] = nested;
180
+ i += consumed;
181
+ }
182
+ else {
183
+ break;
184
+ }
185
+ }
186
+ }
187
+ rows.push(row);
188
+ }
189
+ return [rows, i - start];
190
+ }
191
+ function parseNonUniformArray(lines, start, depth) {
192
+ const indent = ' '.repeat(depth);
193
+ const items = [];
194
+ let i = start;
195
+ while (i < lines.length) {
196
+ const raw = lines[i].replace(/\r$/, '');
197
+ if (raw === '') {
198
+ i++;
199
+ continue;
200
+ }
201
+ const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
202
+ if (content === null)
203
+ break;
204
+ if (content.startsWith('## '))
205
+ break;
206
+ if (content.startsWith('@')) {
207
+ const sp = content.indexOf(' ');
208
+ if (sp > 0) {
209
+ items.push(parseValue(content.slice(sp + 1)));
210
+ }
211
+ i++;
212
+ }
213
+ else {
214
+ break;
215
+ }
216
+ }
217
+ return [items, i - start];
218
+ }
219
+ function parseValue(s) {
220
+ if (s === '-')
221
+ return null;
222
+ if (s === 'true')
223
+ return true;
224
+ if (s === 'false')
225
+ return false;
226
+ if (s === '""')
227
+ return '';
228
+ if (s.length >= 2 && s[0] === '"' && s[s.length - 1] === '"') {
229
+ return s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
230
+ }
231
+ const n = Number(s);
232
+ if (!isNaN(n) && s !== '')
233
+ return n;
234
+ return s;
235
+ }
236
+ //# sourceMappingURL=decode_generic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode_generic.js","sourceRoot":"","sources":["../src/decode_generic.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -5,5 +5,7 @@ export { decode } from './decode.js';
5
5
  export { Session, encodeWithSession } from './session.js';
6
6
  export { encodeDelta } from './delta.js';
7
7
  export { encodeGeneric } from './generic.js';
8
+ export { decodeGeneric } from './decode_generic.js';
8
9
  export { StreamEncoder, type StreamWriter, type StreamOptions } from './stream.js';
10
+ export { GenericStreamEncoder } from './stream_generic.js';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -4,5 +4,7 @@ export { decode } from './decode.js';
4
4
  export { Session, encodeWithSession } from './session.js';
5
5
  export { encodeDelta } from './delta.js';
6
6
  export { encodeGeneric } from './generic.js';
7
+ export { decodeGeneric } from './decode_generic.js';
7
8
  export { StreamEncoder } from './stream.js';
9
+ export { GenericStreamEncoder } from './stream_generic.js';
8
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAyC,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAyC,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,38 @@
1
+ import type { StreamWriter } from './stream.js';
2
+ /**
3
+ * GenericStreamEncoder writes GCF tabular output incrementally as rows arrive.
4
+ * Zero buffering: each row is written immediately. A trailer summary is
5
+ * emitted on close() with the final counts.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const enc = new GenericStreamEncoder({ write: (s) => process.stdout.write(s) });
10
+ * enc.beginArray('employees', ['id', 'name', 'department', 'salary']);
11
+ * enc.writeRow([1, 'Alice', 'Engineering', 95000]);
12
+ * enc.writeRow([2, 'Bob', 'Sales', 72000]);
13
+ * enc.endArray();
14
+ * enc.close();
15
+ * ```
16
+ */
17
+ export declare class GenericStreamEncoder {
18
+ private readonly writer;
19
+ private sections;
20
+ private current;
21
+ constructor(writer: StreamWriter);
22
+ /** Start a tabular array section with deferred count [?]. */
23
+ beginArray(name: string, fields: string[]): void;
24
+ /** Emit a single pipe-separated row immediately. */
25
+ writeRow(values: unknown[]): void;
26
+ /** Close the current array section and record its count. */
27
+ endArray(): void;
28
+ /** Emit a key=value line immediately. */
29
+ writeKV(key: string, value: unknown): void;
30
+ /** Start a nested object section (## key). */
31
+ writeSection(name: string): void;
32
+ /** Emit a primitive array inline: name[N]: val1,val2,val3 */
33
+ writeInlineArray(name: string, values: unknown[]): void;
34
+ /** Emit the ## _summary trailer with final counts. Must be called after all data. */
35
+ close(): void;
36
+ private endArrayInternal;
37
+ }
38
+ //# sourceMappingURL=stream_generic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream_generic.d.ts","sourceRoot":"","sources":["../src/stream_generic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAahD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,OAAO,CAA4B;gBAE/B,MAAM,EAAE,YAAY;IAIhC,6DAA6D;IAC7D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAQhD,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IASjC,4DAA4D;IAC5D,QAAQ,IAAI,IAAI;IAIhB,yCAAyC;IACzC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAI1C,8CAA8C;IAC9C,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOhC,6DAA6D;IAC7D,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI;IAKvD,qFAAqF;IACrF,KAAK,IAAI,IAAI;IAgBb,OAAO,CAAC,gBAAgB;CAOzB"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * GenericStreamEncoder writes GCF tabular output incrementally as rows arrive.
3
+ * Zero buffering: each row is written immediately. A trailer summary is
4
+ * emitted on close() with the final counts.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * const enc = new GenericStreamEncoder({ write: (s) => process.stdout.write(s) });
9
+ * enc.beginArray('employees', ['id', 'name', 'department', 'salary']);
10
+ * enc.writeRow([1, 'Alice', 'Engineering', 95000]);
11
+ * enc.writeRow([2, 'Bob', 'Sales', 72000]);
12
+ * enc.endArray();
13
+ * enc.close();
14
+ * ```
15
+ */
16
+ export class GenericStreamEncoder {
17
+ writer;
18
+ sections = [];
19
+ current = null;
20
+ constructor(writer) {
21
+ this.writer = writer;
22
+ }
23
+ /** Start a tabular array section with deferred count [?]. */
24
+ beginArray(name, fields) {
25
+ if (this.current !== null) {
26
+ this.endArrayInternal();
27
+ }
28
+ this.writer.write(`## ${name} [?]{${fields.join(',')}}\n`);
29
+ this.current = { name, fields, count: 0 };
30
+ }
31
+ /** Emit a single pipe-separated row immediately. */
32
+ writeRow(values) {
33
+ if (this.current === null) {
34
+ return;
35
+ }
36
+ const parts = values.map(formatValue);
37
+ this.writer.write(`${parts.join('|')}\n`);
38
+ this.current.count++;
39
+ }
40
+ /** Close the current array section and record its count. */
41
+ endArray() {
42
+ this.endArrayInternal();
43
+ }
44
+ /** Emit a key=value line immediately. */
45
+ writeKV(key, value) {
46
+ this.writer.write(`${key}=${formatValue(value)}\n`);
47
+ }
48
+ /** Start a nested object section (## key). */
49
+ writeSection(name) {
50
+ if (this.current !== null) {
51
+ this.endArrayInternal();
52
+ }
53
+ this.writer.write(`## ${name}\n`);
54
+ }
55
+ /** Emit a primitive array inline: name[N]: val1,val2,val3 */
56
+ writeInlineArray(name, values) {
57
+ const parts = values.map(formatValue);
58
+ this.writer.write(`${name}[${values.length}]: ${parts.join(',')}\n`);
59
+ }
60
+ /** Emit the ## _summary trailer with final counts. Must be called after all data. */
61
+ close() {
62
+ if (this.current !== null) {
63
+ this.endArrayInternal();
64
+ }
65
+ if (this.sections.length === 0) {
66
+ return;
67
+ }
68
+ let totalRows = 0;
69
+ const sectionParts = [];
70
+ for (const s of this.sections) {
71
+ sectionParts.push(`${s.name}:${s.count}`);
72
+ totalRows += s.count;
73
+ }
74
+ this.writer.write(`## _summary rows=${totalRows} sections=${sectionParts.join(',')}\n`);
75
+ }
76
+ endArrayInternal() {
77
+ if (this.current === null) {
78
+ return;
79
+ }
80
+ this.sections.push({ name: this.current.name, count: this.current.count });
81
+ this.current = null;
82
+ }
83
+ }
84
+ function formatValue(v) {
85
+ if (v === null || v === undefined) {
86
+ return '-';
87
+ }
88
+ if (typeof v === 'boolean') {
89
+ return v ? 'true' : 'false';
90
+ }
91
+ if (typeof v === 'number') {
92
+ return String(v);
93
+ }
94
+ if (typeof v === 'string') {
95
+ if (v === '') {
96
+ return '""';
97
+ }
98
+ if (v.includes('|') || v.includes('\n')) {
99
+ return `"${v.replace(/"/g, '\\"')}"`;
100
+ }
101
+ return v;
102
+ }
103
+ return String(v);
104
+ }
105
+ //# sourceMappingURL=stream_generic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream_generic.js","sourceRoot":"","sources":["../src/stream_generic.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackwell-systems/gcf",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "TypeScript implementation of GCF (Graph Compact Format) - token-optimized wire format for LLM tool responses",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,237 @@
1
+ import { decode } from './decode.js';
2
+
3
+ /**
4
+ * Decode any GCF text (tabular or graph profile) back into a JS value.
5
+ * Returns objects, arrays, and primitives matching the original structure.
6
+ *
7
+ * If the input starts with "GCF " (graph profile), falls back to decode()
8
+ * and returns the Payload as a plain object.
9
+ */
10
+ export function decodeGeneric(input: string): any {
11
+ input = input.trimEnd();
12
+ if (!input) return null;
13
+
14
+ const lines = input.split('\n');
15
+
16
+ // Graph profile fallback.
17
+ if (lines[0].startsWith('GCF ')) {
18
+ const p = decode(input);
19
+ return {
20
+ tool: p.tool,
21
+ tokenBudget: p.tokenBudget,
22
+ tokensUsed: p.tokensUsed,
23
+ packRoot: p.packRoot ?? '',
24
+ symbols: p.symbols.map(s => ({
25
+ qualifiedName: s.qualifiedName,
26
+ kind: s.kind,
27
+ score: s.score,
28
+ provenance: s.provenance,
29
+ distance: s.distance,
30
+ })),
31
+ edges: p.edges.map(e => ({
32
+ source: e.source,
33
+ target: e.target,
34
+ edgeType: e.edgeType,
35
+ ...(e.status ? { status: e.status } : {}),
36
+ })),
37
+ };
38
+ }
39
+
40
+ const result: Record<string, any> = {};
41
+ parseObject(lines, 0, 0, result);
42
+ return result;
43
+ }
44
+
45
+ function parseObject(lines: string[], start: number, depth: number, out: Record<string, any>): number {
46
+ const indent = ' '.repeat(depth);
47
+ let i = start;
48
+
49
+ while (i < lines.length) {
50
+ const raw = lines[i].replace(/\r$/, '');
51
+ if (raw === '' || raw.startsWith('# ')) { i++; continue; }
52
+
53
+ if (depth > 0 && !raw.startsWith(indent)) break;
54
+
55
+ const content = depth > 0 ? raw.slice(indent.length) : raw;
56
+
57
+ // Skip _summary.
58
+ if (content.startsWith('## _summary')) { i++; continue; }
59
+
60
+ // Tabular array or section header.
61
+ if (content.startsWith('## ')) {
62
+ const header = content.slice(3);
63
+ const bracketIdx = header.indexOf(' [');
64
+
65
+ if (bracketIdx >= 0) {
66
+ const name = header.slice(0, bracketIdx);
67
+ const rest = header.slice(bracketIdx + 2);
68
+ const closeBracket = rest.indexOf(']');
69
+
70
+ if (closeBracket >= 0) {
71
+ const afterBracket = rest.slice(closeBracket + 1);
72
+
73
+ if (afterBracket.startsWith('{')) {
74
+ // Tabular with fields.
75
+ const fieldEnd = afterBracket.indexOf('}');
76
+ if (fieldEnd >= 0) {
77
+ const fields = afterBracket.slice(1, fieldEnd).split(',');
78
+ i++;
79
+ const [rows, consumed] = parseTabularRows(lines, i, depth, fields);
80
+ out[name] = rows;
81
+ i += consumed;
82
+ continue;
83
+ }
84
+ } else {
85
+ const countStr = rest.slice(0, closeBracket);
86
+ if (countStr === '0') {
87
+ out[name] = [];
88
+ i++;
89
+ continue;
90
+ }
91
+ // Non-uniform array.
92
+ i++;
93
+ const [items, consumed] = parseNonUniformArray(lines, i, depth);
94
+ out[name] = items;
95
+ i += consumed;
96
+ continue;
97
+ }
98
+ }
99
+ }
100
+
101
+ // Plain section header.
102
+ let name = header;
103
+ const bi = name.indexOf(' [');
104
+ if (bi >= 0) name = name.slice(0, bi);
105
+ i++;
106
+ const nested: Record<string, any> = {};
107
+ const consumed = parseObject(lines, i, depth + 1, nested);
108
+ out[name] = nested;
109
+ i += consumed;
110
+ continue;
111
+ }
112
+
113
+ // Inline primitive array: name[N]: val1,val2,...
114
+ const bracketIdx = content.indexOf('[');
115
+ if (bracketIdx > 0) {
116
+ const colonIdx = content.indexOf(']: ');
117
+ if (colonIdx > bracketIdx) {
118
+ const name = content.slice(0, bracketIdx);
119
+ const valsStr = content.slice(colonIdx + 3);
120
+ out[name] = valsStr.split(',').map(v => parseValue(v.trim()));
121
+ i++;
122
+ continue;
123
+ }
124
+ }
125
+
126
+ // Key=value.
127
+ const eqIdx = content.indexOf('=');
128
+ if (eqIdx > 0) {
129
+ const key = content.slice(0, eqIdx);
130
+ const val = content.slice(eqIdx + 1);
131
+ out[key] = parseValue(val);
132
+ i++;
133
+ continue;
134
+ }
135
+
136
+ i++;
137
+ }
138
+
139
+ return i - start;
140
+ }
141
+
142
+ function parseTabularRows(lines: string[], start: number, depth: number, fields: string[]): [any[], number] {
143
+ const indent = ' '.repeat(depth);
144
+ const rows: any[] = [];
145
+ let i = start;
146
+
147
+ while (i < lines.length) {
148
+ const raw = lines[i].replace(/\r$/, '');
149
+ if (raw === '') { i++; continue; }
150
+
151
+ const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
152
+ if (content === null) break;
153
+ if (content.startsWith('## ')) break;
154
+ if (content.startsWith('# ')) { i++; continue; }
155
+
156
+ let rowData = content;
157
+ let hasNested = false;
158
+ if (rowData.startsWith('@')) {
159
+ const sp = rowData.indexOf(' ');
160
+ if (sp > 0) {
161
+ rowData = rowData.slice(sp + 1);
162
+ hasNested = true;
163
+ }
164
+ }
165
+
166
+ const vals = rowData.split('|');
167
+ const row: Record<string, any> = {};
168
+ for (let j = 0; j < fields.length; j++) {
169
+ row[fields[j]] = j < vals.length ? parseValue(vals[j]) : null;
170
+ }
171
+
172
+ i++;
173
+
174
+ if (hasNested) {
175
+ const nestedIndent = indent + ' ';
176
+ while (i < lines.length) {
177
+ const nl = lines[i].replace(/\r$/, '');
178
+ if (!nl.startsWith(nestedIndent)) break;
179
+ const nc = nl.slice(nestedIndent.length);
180
+
181
+ if (nc.startsWith('.')) {
182
+ const fieldName = nc.slice(1);
183
+ i++;
184
+ const nested: Record<string, any> = {};
185
+ const consumed = parseObject(lines, i, depth + 2, nested);
186
+ row[fieldName] = nested;
187
+ i += consumed;
188
+ } else {
189
+ break;
190
+ }
191
+ }
192
+ }
193
+
194
+ rows.push(row);
195
+ }
196
+
197
+ return [rows, i - start];
198
+ }
199
+
200
+ function parseNonUniformArray(lines: string[], start: number, depth: number): [any[], number] {
201
+ const indent = ' '.repeat(depth);
202
+ const items: any[] = [];
203
+ let i = start;
204
+
205
+ while (i < lines.length) {
206
+ const raw = lines[i].replace(/\r$/, '');
207
+ if (raw === '') { i++; continue; }
208
+ const content = depth > 0 ? (raw.startsWith(indent) ? raw.slice(indent.length) : null) : raw;
209
+ if (content === null) break;
210
+ if (content.startsWith('## ')) break;
211
+
212
+ if (content.startsWith('@')) {
213
+ const sp = content.indexOf(' ');
214
+ if (sp > 0) {
215
+ items.push(parseValue(content.slice(sp + 1)));
216
+ }
217
+ i++;
218
+ } else {
219
+ break;
220
+ }
221
+ }
222
+
223
+ return [items, i - start];
224
+ }
225
+
226
+ function parseValue(s: string): any {
227
+ if (s === '-') return null;
228
+ if (s === 'true') return true;
229
+ if (s === 'false') return false;
230
+ if (s === '""') return '';
231
+ if (s.length >= 2 && s[0] === '"' && s[s.length - 1] === '"') {
232
+ return s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
233
+ }
234
+ const n = Number(s);
235
+ if (!isNaN(n) && s !== '') return n;
236
+ return s;
237
+ }
package/src/index.ts CHANGED
@@ -5,4 +5,6 @@ export { decode } from './decode.js';
5
5
  export { Session, encodeWithSession } from './session.js';
6
6
  export { encodeDelta } from './delta.js';
7
7
  export { encodeGeneric } from './generic.js';
8
+ export { decodeGeneric } from './decode_generic.js';
8
9
  export { StreamEncoder, type StreamWriter, type StreamOptions } from './stream.js';
10
+ export { GenericStreamEncoder } from './stream_generic.js';
@@ -0,0 +1,127 @@
1
+ import type { StreamWriter } from './stream.js';
2
+
3
+ interface SectionCount {
4
+ name: string;
5
+ count: number;
6
+ }
7
+
8
+ interface ActiveArray {
9
+ name: string;
10
+ fields: string[];
11
+ count: number;
12
+ }
13
+
14
+ /**
15
+ * GenericStreamEncoder writes GCF tabular output incrementally as rows arrive.
16
+ * Zero buffering: each row is written immediately. A trailer summary is
17
+ * emitted on close() with the final counts.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const enc = new GenericStreamEncoder({ write: (s) => process.stdout.write(s) });
22
+ * enc.beginArray('employees', ['id', 'name', 'department', 'salary']);
23
+ * enc.writeRow([1, 'Alice', 'Engineering', 95000]);
24
+ * enc.writeRow([2, 'Bob', 'Sales', 72000]);
25
+ * enc.endArray();
26
+ * enc.close();
27
+ * ```
28
+ */
29
+ export class GenericStreamEncoder {
30
+ private readonly writer: StreamWriter;
31
+ private sections: SectionCount[] = [];
32
+ private current: ActiveArray | null = null;
33
+
34
+ constructor(writer: StreamWriter) {
35
+ this.writer = writer;
36
+ }
37
+
38
+ /** Start a tabular array section with deferred count [?]. */
39
+ beginArray(name: string, fields: string[]): void {
40
+ if (this.current !== null) {
41
+ this.endArrayInternal();
42
+ }
43
+ this.writer.write(`## ${name} [?]{${fields.join(',')}}\n`);
44
+ this.current = { name, fields, count: 0 };
45
+ }
46
+
47
+ /** Emit a single pipe-separated row immediately. */
48
+ writeRow(values: unknown[]): void {
49
+ if (this.current === null) {
50
+ return;
51
+ }
52
+ const parts = values.map(formatValue);
53
+ this.writer.write(`${parts.join('|')}\n`);
54
+ this.current.count++;
55
+ }
56
+
57
+ /** Close the current array section and record its count. */
58
+ endArray(): void {
59
+ this.endArrayInternal();
60
+ }
61
+
62
+ /** Emit a key=value line immediately. */
63
+ writeKV(key: string, value: unknown): void {
64
+ this.writer.write(`${key}=${formatValue(value)}\n`);
65
+ }
66
+
67
+ /** Start a nested object section (## key). */
68
+ writeSection(name: string): void {
69
+ if (this.current !== null) {
70
+ this.endArrayInternal();
71
+ }
72
+ this.writer.write(`## ${name}\n`);
73
+ }
74
+
75
+ /** Emit a primitive array inline: name[N]: val1,val2,val3 */
76
+ writeInlineArray(name: string, values: unknown[]): void {
77
+ const parts = values.map(formatValue);
78
+ this.writer.write(`${name}[${values.length}]: ${parts.join(',')}\n`);
79
+ }
80
+
81
+ /** Emit the ## _summary trailer with final counts. Must be called after all data. */
82
+ close(): void {
83
+ if (this.current !== null) {
84
+ this.endArrayInternal();
85
+ }
86
+ if (this.sections.length === 0) {
87
+ return;
88
+ }
89
+ let totalRows = 0;
90
+ const sectionParts: string[] = [];
91
+ for (const s of this.sections) {
92
+ sectionParts.push(`${s.name}:${s.count}`);
93
+ totalRows += s.count;
94
+ }
95
+ this.writer.write(`## _summary rows=${totalRows} sections=${sectionParts.join(',')}\n`);
96
+ }
97
+
98
+ private endArrayInternal(): void {
99
+ if (this.current === null) {
100
+ return;
101
+ }
102
+ this.sections.push({ name: this.current.name, count: this.current.count });
103
+ this.current = null;
104
+ }
105
+ }
106
+
107
+ function formatValue(v: unknown): string {
108
+ if (v === null || v === undefined) {
109
+ return '-';
110
+ }
111
+ if (typeof v === 'boolean') {
112
+ return v ? 'true' : 'false';
113
+ }
114
+ if (typeof v === 'number') {
115
+ return String(v);
116
+ }
117
+ if (typeof v === 'string') {
118
+ if (v === '') {
119
+ return '""';
120
+ }
121
+ if (v.includes('|') || v.includes('\n')) {
122
+ return `"${v.replace(/"/g, '\\"')}"`;
123
+ }
124
+ return v;
125
+ }
126
+ return String(v);
127
+ }