aontu 0.56.0 → 0.57.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.
Files changed (69) hide show
  1. package/dist/aontu.d.ts +2 -1
  2. package/dist/aontu.js +1 -1
  3. package/dist/aontu.js.map +1 -1
  4. package/dist/cli.d.ts +5 -1
  5. package/dist/cli.js +89 -18
  6. package/dist/cli.js.map +1 -1
  7. package/dist/escape.d.ts +5 -0
  8. package/dist/escape.js +455 -0
  9. package/dist/escape.js.map +1 -0
  10. package/dist/format.d.ts +9 -0
  11. package/dist/format.js +544 -55
  12. package/dist/format.js.map +1 -1
  13. package/dist/hints.js +35 -1
  14. package/dist/hints.js.map +1 -1
  15. package/dist/lang.js +20 -1
  16. package/dist/lang.js.map +1 -1
  17. package/dist/lsp.d.ts +1 -1
  18. package/dist/lsp.js +3 -3
  19. package/dist/lsp.js.map +1 -1
  20. package/dist/mcp-server.js +2 -2
  21. package/dist/mcp-server.js.map +1 -1
  22. package/dist/mod-tool.js +8 -7
  23. package/dist/mod-tool.js.map +1 -1
  24. package/dist/mod.js +6 -6
  25. package/dist/mod.js.map +1 -1
  26. package/dist/sigdecl.js +1 -1
  27. package/dist/sigdecl.js.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/dist/val/EachFuncVal.js +1 -2
  30. package/dist/val/EachFuncVal.js.map +1 -1
  31. package/dist/val/EmitFuncVal.d.ts +23 -0
  32. package/dist/val/EmitFuncVal.js +261 -0
  33. package/dist/val/EmitFuncVal.js.map +1 -0
  34. package/dist/val/FilterFuncVal.js +1 -2
  35. package/dist/val/FilterFuncVal.js.map +1 -1
  36. package/dist/val/FuncBaseVal.d.ts +1 -0
  37. package/dist/val/FuncBaseVal.js +16 -0
  38. package/dist/val/FuncBaseVal.js.map +1 -1
  39. package/dist/val/PackFuncVal.js +1 -2
  40. package/dist/val/PackFuncVal.js.map +1 -1
  41. package/dist/val/PlaceVal.d.ts +3 -1
  42. package/dist/val/PlaceVal.js +8 -6
  43. package/dist/val/PlaceVal.js.map +1 -1
  44. package/dist/val/StrFuncVal.d.ts +32 -0
  45. package/dist/val/StrFuncVal.js +292 -0
  46. package/dist/val/StrFuncVal.js.map +1 -0
  47. package/grammar/aontu.abnf +4 -3
  48. package/grammar/aontu.gbnf +4 -3
  49. package/grammar/aontu.lark +4 -3
  50. package/grammar/aontu.tmLanguage.json +1 -1
  51. package/package.json +1 -1
  52. package/src/aontu.ts +2 -1
  53. package/src/cli.ts +113 -20
  54. package/src/escape.ts +371 -0
  55. package/src/format.ts +638 -56
  56. package/src/hints.ts +48 -1
  57. package/src/lang.ts +24 -1
  58. package/src/lsp.ts +3 -3
  59. package/src/mcp-server.ts +3 -2
  60. package/src/mod-tool.ts +9 -8
  61. package/src/mod.ts +6 -6
  62. package/src/sigdecl.ts +1 -1
  63. package/src/val/EachFuncVal.ts +1 -3
  64. package/src/val/EmitFuncVal.ts +401 -0
  65. package/src/val/FilterFuncVal.ts +1 -3
  66. package/src/val/FuncBaseVal.ts +18 -0
  67. package/src/val/PackFuncVal.ts +1 -3
  68. package/src/val/PlaceVal.ts +8 -6
  69. package/src/val/StrFuncVal.ts +334 -0
@@ -0,0 +1,292 @@
1
+ "use strict";
2
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.SplitFuncVal = exports.RepFuncVal = exports.UscFuncVal = exports.EscFuncVal = void 0;
5
+ const err_1 = require("../err");
6
+ const escape_1 = require("../escape");
7
+ const StringVal_1 = require("./StringVal");
8
+ const ListVal_1 = require("./ListVal");
9
+ const FuncBaseVal_1 = require("./FuncBaseVal");
10
+ const ConstraintVal_1 = require("./ConstraintVal");
11
+ // The string a value carries, or undefined when it is not a string.
12
+ // Mirrors `stringLeaf` in ConstraintVal: a path is a string too, so a
13
+ // spelled address may be escaped and split like any other text.
14
+ function textOf(v) {
15
+ const s = v;
16
+ return true === s?.isScalar && 'string' === typeof s.peg ? s.peg : undefined;
17
+ }
18
+ // The variant an optional second argument names: '' for the absent
19
+ // argument (the C/JSON convention), or undefined when it is not a
20
+ // variant name.
21
+ function variantOf(v) {
22
+ if (null == v) {
23
+ return '';
24
+ }
25
+ const s = textOf(v);
26
+ return undefined !== s && (0, escape_1.isEscVariant)(s) ? s : undefined;
27
+ }
28
+ // The number of CAPTURING groups in a normalised pattern. Counted by
29
+ // scanning rather than by asking either host, because the count decides
30
+ // whether a substitution is refused and the two ports must refuse the
31
+ // same ones. The subset admits no named groups, so a capturing group is
32
+ // exactly `(` that is not `(?`.
33
+ function reGroupCount(norm) {
34
+ let count = 0;
35
+ let inClass = false;
36
+ for (let i = 0; i < norm.length; i++) {
37
+ const c = norm[i];
38
+ if ('\\' === c) {
39
+ i++;
40
+ continue;
41
+ }
42
+ if (inClass) {
43
+ if (']' === c) {
44
+ inClass = false;
45
+ }
46
+ continue;
47
+ }
48
+ if ('[' === c) {
49
+ inClass = true;
50
+ continue;
51
+ }
52
+ if ('(' === c && '?' !== norm[i + 1]) {
53
+ count++;
54
+ }
55
+ }
56
+ return count;
57
+ }
58
+ // One code point at `at`, as a count of UTF-16 units: the "advance by
59
+ // one rune" Go's matching loop performs after an empty match.
60
+ function stepAt(src, at) {
61
+ const c = src.codePointAt(at);
62
+ return undefined === c ? 1 : (0xFFFF < c ? 2 : 1);
63
+ }
64
+ // Every match of `re` in `src`, as [start, end, ...group pairs], under
65
+ // Go's rule: scanning resumes at the end of each match, and an empty
66
+ // match ADJACENT to the previous match's end is skipped rather than
67
+ // delivered. Written out because JavaScript has no equivalent and the
68
+ // difference is visible in `rep("aa", "a*", "-")`.
69
+ function allMatches(src, re) {
70
+ const out = [];
71
+ // Its own scanner, so `lastIndex` is this walk's to move: the caller's
72
+ // regexp is shared with whatever else holds it.
73
+ const scan = new RegExp(re.source, re.flags);
74
+ let pos = 0;
75
+ let prevEnd = -1;
76
+ while (pos <= src.length) {
77
+ scan.lastIndex = pos;
78
+ const m = scan.exec(src);
79
+ if (null === m) {
80
+ break;
81
+ }
82
+ const start = m.index;
83
+ const end = start + m[0].length;
84
+ let accept = true;
85
+ if (end === pos) {
86
+ if (start === prevEnd) {
87
+ accept = false;
88
+ }
89
+ pos += stepAt(src, pos);
90
+ }
91
+ else {
92
+ pos = end;
93
+ }
94
+ prevEnd = end;
95
+ if (accept) {
96
+ out.push(m);
97
+ }
98
+ }
99
+ return out;
100
+ }
101
+ // A substitution template expanded against one match. `$1`..`$9` are
102
+ // the numbered groups, `$&` the whole match and `$$` a literal `$`;
103
+ // anything else after a `$` names nothing, and naming nothing is a
104
+ // REFUSAL rather than a silent literal. A group the pattern does not
105
+ // have is the same refusal -- a generator that expands it to the empty
106
+ // string writes a file with a hole in it and says nothing.
107
+ function expandSub(sub, m, groups) {
108
+ let out = '';
109
+ for (let i = 0; i < sub.length; i++) {
110
+ if ('$' !== sub[i]) {
111
+ out += sub[i];
112
+ continue;
113
+ }
114
+ const n = sub[i + 1];
115
+ if ('$' === n) {
116
+ out += '$';
117
+ i++;
118
+ continue;
119
+ }
120
+ if ('&' === n) {
121
+ out += m[0];
122
+ i++;
123
+ continue;
124
+ }
125
+ if (undefined === n || '1' > n || '9' < n) {
126
+ return undefined;
127
+ }
128
+ const g = n.charCodeAt(0) - 0x30;
129
+ if (groups < g) {
130
+ return undefined;
131
+ }
132
+ out += m[g] ?? '';
133
+ i++;
134
+ }
135
+ return out;
136
+ }
137
+ // The fields `re` cuts `src` into, under Go's Split: an empty match at
138
+ // the very start opens no field, and a match ending at the end of the
139
+ // input closes none.
140
+ function splitRe(src, re) {
141
+ const out = [];
142
+ let beg = 0;
143
+ let end = 0;
144
+ for (const m of allMatches(src, re)) {
145
+ end = m.index;
146
+ if (0 !== m.index + m[0].length) {
147
+ out.push(src.slice(beg, end));
148
+ }
149
+ beg = m.index + m[0].length;
150
+ }
151
+ if (end !== src.length) {
152
+ out.push(src.slice(beg));
153
+ }
154
+ return out;
155
+ }
156
+ // An EMPTY separator yields the CODE POINTS, not the UTF-16 units a
157
+ // host split would give: `split("é", "")` is one field in both ports.
158
+ function splitLiteral(src, sep) {
159
+ if ('' === sep) {
160
+ return [...src];
161
+ }
162
+ return src.split(sep);
163
+ }
164
+ // The compiled form of a pattern argument, or the code naming what is
165
+ // wrong with it.
166
+ function compileRe(src) {
167
+ const [norm, why] = (0, ConstraintVal_1.normaliseRe)(src);
168
+ if ('' !== why) {
169
+ return 'rep_pattern';
170
+ }
171
+ try {
172
+ // The `u` flag is REQUIRED for parity, exactly as it is for `re()`:
173
+ // without it JavaScript counts UTF-16 units where Go counts code
174
+ // points.
175
+ return new RegExp(norm, 'gu');
176
+ }
177
+ catch (e) {
178
+ return 'rep_pattern';
179
+ }
180
+ }
181
+ class EscFuncVal extends FuncBaseVal_1.FuncBaseVal {
182
+ constructor(spec, ctx) {
183
+ super(spec, ctx);
184
+ this.isEscFunc = true;
185
+ }
186
+ make(_ctx, spec) { return new EscFuncVal(spec); }
187
+ funcname() { return 'esc'; }
188
+ resolve(ctx, args) {
189
+ const src = textOf(args?.[0]);
190
+ if (undefined === src) {
191
+ return (0, err_1.makeNilErr)(ctx, 'invalid-arg', this);
192
+ }
193
+ const variant = variantOf(args?.[1]);
194
+ if (undefined === variant) {
195
+ return (0, err_1.makeNilErr)(ctx, 'esc_variant', this);
196
+ }
197
+ return this.place(new StringVal_1.StringVal({ peg: (0, escape_1.escapeText)(src, variant) }, ctx));
198
+ }
199
+ } /* node:coverage ignore next 2 */
200
+ exports.EscFuncVal = EscFuncVal;
201
+ class UscFuncVal extends FuncBaseVal_1.FuncBaseVal {
202
+ constructor(spec, ctx) {
203
+ super(spec, ctx);
204
+ this.isUscFunc = true;
205
+ }
206
+ make(_ctx, spec) { return new UscFuncVal(spec); }
207
+ funcname() { return 'usc'; }
208
+ resolve(ctx, args) {
209
+ const src = textOf(args?.[0]);
210
+ if (undefined === src) {
211
+ return (0, err_1.makeNilErr)(ctx, 'invalid-arg', this);
212
+ }
213
+ const variant = variantOf(args?.[1]);
214
+ if (undefined === variant) {
215
+ return (0, err_1.makeNilErr)(ctx, 'esc_variant', this);
216
+ }
217
+ const [out, ok] = (0, escape_1.unescapeText)(src, variant);
218
+ if (!ok) {
219
+ return (0, err_1.makeNilErr)(ctx, 'usc_malformed', this);
220
+ }
221
+ return this.place(new StringVal_1.StringVal({ peg: out }, ctx));
222
+ }
223
+ } /* node:coverage ignore next 2 */
224
+ exports.UscFuncVal = UscFuncVal;
225
+ class RepFuncVal extends FuncBaseVal_1.FuncBaseVal {
226
+ constructor(spec, ctx) {
227
+ super(spec, ctx);
228
+ this.isRepFunc = true;
229
+ }
230
+ make(_ctx, spec) { return new RepFuncVal(spec); }
231
+ funcname() { return 'rep'; }
232
+ resolve(ctx, args) {
233
+ const src = textOf(args?.[0]);
234
+ const pat = textOf(args?.[1]);
235
+ const sub = textOf(args?.[2]);
236
+ if (undefined === src || undefined === pat || undefined === sub) {
237
+ return (0, err_1.makeNilErr)(ctx, 'invalid-arg', this);
238
+ }
239
+ const re = compileRe(pat);
240
+ if ('string' === typeof re) {
241
+ return (0, err_1.makeNilErr)(ctx, re, this);
242
+ }
243
+ const groups = reGroupCount(re.source);
244
+ let out = '';
245
+ let at = 0;
246
+ for (const m of allMatches(src, re)) {
247
+ const piece = expandSub(sub, m, groups);
248
+ if (undefined === piece) {
249
+ return (0, err_1.makeNilErr)(ctx, 'rep_sub', this);
250
+ }
251
+ out += src.slice(at, m.index) + piece;
252
+ at = m.index + m[0].length;
253
+ }
254
+ out += src.slice(at);
255
+ return this.place(new StringVal_1.StringVal({ peg: out }, ctx));
256
+ }
257
+ } /* node:coverage ignore next 2 */
258
+ exports.RepFuncVal = RepFuncVal;
259
+ class SplitFuncVal extends FuncBaseVal_1.FuncBaseVal {
260
+ constructor(spec, ctx) {
261
+ super(spec, ctx);
262
+ this.isSplitFunc = true;
263
+ }
264
+ make(_ctx, spec) { return new SplitFuncVal(spec); }
265
+ funcname() { return 'split'; }
266
+ resolve(ctx, args) {
267
+ const src = textOf(args?.[0]);
268
+ if (undefined === src) {
269
+ return (0, err_1.makeNilErr)(ctx, 'invalid-arg', this);
270
+ }
271
+ // A PLAIN STRING IS A LITERAL and an `re(…)` is a pattern. The
272
+ // asymmetry with `rep` is deliberate: splitting is usually on a
273
+ // literal, replacing is usually by pattern, and it removes the trap
274
+ // where `split(v, ".")` silently cuts between every character.
275
+ const sep = args?.[1];
276
+ const lit = textOf(sep);
277
+ let fields;
278
+ if (undefined !== lit) {
279
+ fields = splitLiteral(src, lit);
280
+ }
281
+ else if (true === sep?.isConstraint && 1 === sep.res?.length) {
282
+ fields = splitRe(src, new RegExp(sep.res[0].re.source, 'gu'));
283
+ }
284
+ else {
285
+ return (0, err_1.makeNilErr)(ctx, 'split_sep', this);
286
+ }
287
+ const peg = fields.map((f, i) => new StringVal_1.StringVal({ peg: f }, ctx.descend(String(i))));
288
+ return this.place(new ListVal_1.ListVal({ peg }, ctx));
289
+ }
290
+ } /* node:coverage ignore next 8 */
291
+ exports.SplitFuncVal = SplitFuncVal;
292
+ //# sourceMappingURL=StrFuncVal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StrFuncVal.js","sourceRoot":"","sources":["../../src/val/StrFuncVal.ts"],"names":[],"mappings":"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"}
@@ -82,7 +82,7 @@ func = name %s"(" ws [ value *( ws %s"," ws value ) ws ] %s")"
82
82
  ; listed before `re`, and `rem` before `re` as well.
83
83
  name = %s"above" / %s"acyclic" / %s"add" / %s"below" / %s"close"
84
84
  / %s"copy" / %s"deprecate" / %s"div"
85
- / %s"each" / %s"filter" / %s"greatest"
85
+ / %s"each" / %s"emit" / %s"esc" / %s"filter" / %s"greatest"
86
86
  / %s"hide"
87
87
  / %s"inverse" / %s"join" / %s"key" / %s"least"
88
88
  / %s"length" / %s"list" / %s"lower" / %s"map" / %s"match"
@@ -90,10 +90,11 @@ name = %s"above" / %s"acyclic" / %s"add" / %s"below" / %s"close"
90
90
  / %s"mul" / %s"must"
91
91
  / %s"neq"
92
92
  / %s"open" / %s"pack" / %s"path" / %s"pick" / %s"pref"
93
- / %s"refer" / %s"rel" / %s"rem" / %s"re" / %s"super"
93
+ / %s"refer" / %s"rel" / %s"rem" / %s"rep" / %s"re" / %s"split"
94
+ / %s"super"
94
95
  / %s"sub" / %s"sum"
95
96
  / %s"type"
96
- / %s"unique" / %s"upper"
97
+ / %s"unique" / %s"upper" / %s"usc"
97
98
 
98
99
  ; A path reference, absolute from the document root. At least one
99
100
  ; segment: a bare `$` is an incomplete expression, not a reference.
@@ -82,16 +82,17 @@ func ::= name "(" ws ( value ( ws "," ws value )* ws )? ")"
82
82
 
83
83
  name ::=
84
84
  "above" | "acyclic" | "add" | "below" | "close" | "copy" | "deprecate" | "div"
85
- | "each" | "filter" | "greatest"
85
+ | "each" | "emit" | "esc" | "filter" | "greatest"
86
86
  | "hide"
87
87
  | "inverse" | "join" | "key" | "least"
88
88
  | "length" | "list" | "lower" | "map" | "match" | "max" | "min" | "mod" | "move"
89
89
  | "mul" | "must"
90
90
  | "neq"
91
- | "open" | "pack" | "path" | "pick" | "pref" | "refer" | "rel" | "rem" | "re" | "super"
91
+ | "open" | "pack" | "path" | "pick" | "pref" | "refer" | "rel" | "rem" | "rep" | "re"
92
+ | "split" | "super"
92
93
  | "sub" | "sum"
93
94
  | "type"
94
- | "unique" | "upper"
95
+ | "unique" | "upper" | "usc"
95
96
 
96
97
  # A path reference, absolute from the document root.
97
98
  # At least one segment: a bare `$` is an incomplete expression, not a
@@ -63,16 +63,17 @@ element: spread
63
63
  func: name "(" [value ("," value)*] ")"
64
64
 
65
65
  name: "above" | "acyclic" | "add" | "below" | "close" | "copy" | "deprecate" | "div"
66
- | "each" | "filter" | "greatest"
66
+ | "each" | "emit" | "esc" | "filter" | "greatest"
67
67
  | "hide"
68
68
  | "inverse" | "join" | "key" | "least"
69
69
  | "length" | "list" | "lower" | "map" | "match" | "max" | "min" | "mod" | "move"
70
70
  | "mul" | "must"
71
71
  | "neq"
72
- | "open" | "pack" | "path" | "pick" | "pref" | "refer" | "rel" | "rem" | "re" | "super"
72
+ | "open" | "pack" | "path" | "pick" | "pref" | "refer" | "rel" | "rem" | "rep" | "re"
73
+ | "split" | "super"
73
74
  | "sub" | "sum"
74
75
  | "type"
75
- | "unique" | "upper"
76
+ | "unique" | "upper" | "usc"
76
77
 
77
78
  // At least one segment: a bare `$` is an incomplete expression.
78
79
  //
@@ -111,7 +111,7 @@
111
111
 
112
112
  "builtin": {
113
113
  "comment": "The engine's function registry. `func: name \"(\" ...` in aontu.lark — only these names can be called, so a call to anything else is deliberately left uncoloured rather than dressed up as valid. The prefix family is spelled longest-first (refer|rel|rem|re) to keep the intent legible; the trailing \\b already makes the order immaterial, since a short alternative that swallowed the stem fails the boundary and backtracks.",
114
- "match": "\\b(above|acyclic|add|below|close|copy|deprecate|div|each|filter|greatest|hide|inverse|join|key|least|length|list|lower|map|match|max|min|mod|move|mul|must|neq|open|pack|path|pick|pref|refer|rel|rem|re|sub|sum|super|type|unique|upper)\\b(?=\\s*\\()",
114
+ "match": "\\b(above|acyclic|add|below|close|copy|deprecate|div|each|emit|esc|filter|greatest|hide|inverse|join|key|least|length|list|lower|map|match|max|min|mod|move|mul|must|neq|open|pack|path|pick|pref|refer|rel|rem|rep|re|split|sub|sum|super|type|unique|upper|usc)\\b(?=\\s*\\()",
115
115
  "captures": {
116
116
  "1": { "name": "support.function.builtin.aontu" }
117
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aontu",
3
- "version": "0.56.0",
3
+ "version": "0.57.0",
4
4
  "main": "dist/aontu.js",
5
5
  "type": "commonjs",
6
6
  "types": "dist/aontu.d.ts",
package/src/aontu.ts CHANGED
@@ -26,6 +26,7 @@ import { graphOf } from './graph'
26
26
  import { relationCheck, relationErrors } from './relation'
27
27
  import { view, viewSet, viewTree } from './view'
28
28
  import { format, unifiedDiff } from './format'
29
+ export type { LintFinding, FormatReport, FormatOptions } from './format'
29
30
 
30
31
 
31
32
  // VERSION is the Aontu npm package version, and mirrors
@@ -35,7 +36,7 @@ import { format, unifiedDiff } from './format'
35
36
  // Kept in step with package.json by the `version` npm lifecycle script,
36
37
  // which runs on `npm version` / `npm run repo-bump`. version.test.ts
37
38
  // fails if the two ever drift.
38
- const VERSION = '0.56.0'
39
+ const VERSION = '0.57.0'
39
40
 
40
41
 
41
42
  // A module file's VALUE, as far as it goes. COLLECTED, not raised: a
package/src/cli.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  import { evalFailure } from './query'
13
13
  // __importStar downlevel helper, whose branches no supported Node takes.
14
14
  import {
15
- mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync,
15
+ existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync,
16
16
  } from 'node:fs'
17
17
  import { basename, dirname, join, resolve } from 'node:path'
18
18
  import { tmpdir } from 'node:os'
@@ -25,6 +25,8 @@ import {
25
25
  get, why, patch, agentsMd,
26
26
  } from './aontu'
27
27
  import { sarifReport } from './report-sarif'
28
+ import { main as lspMain } from './lsp-server'
29
+ import { main as mcpMain } from './mcp-server'
28
30
  import { jsonSchema } from './jsonschema'
29
31
  import { modTidy, modVerify, modVendor, modManifest } from './mod-tool'
30
32
  import type {
@@ -72,7 +74,9 @@ const HELP = `Usage: aontu [options] [file]
72
74
  aontu why <path> [options] <file>
73
75
  aontu set <path>=<value>... --entry <file> --overlay <file>
74
76
  aontu agentsmd [--write <AGENTS.md>] <file>
75
- aontu fmt [-w|-l|--check|-d] <file>...
77
+ aontu fmt [-w|-l|--check|-d|--lint] <file>...
78
+ aontu lsp
79
+ aontu mcp [--root <dir>]
76
80
 
77
81
  Evaluate an Aontu source file and print the result as JSON.
78
82
  With no file on an interactive terminal, start a REPL.
@@ -106,10 +110,10 @@ Mod options:
106
110
 
107
111
  Mod subcommands:
108
112
  tidy Resolve the module closure by minimum version selection and
109
- rewrite mod-lock.aon in canonical form
110
- verify Check every locked module still means what mod-lock.aon
113
+ rewrite aontu_meta/mod-lock.aon in canonical form
114
+ verify Check every locked module still means what the lockfile
111
115
  pins, and change nothing (the CI gate; tidy rewrites)
112
- vendor Materialise the locked closure into aon_vendor/
116
+ vendor Materialise the locked closure into aontu_meta/vendor/
113
117
  manifest Print the OCI artifact a publish would push, gated on the
114
118
  breaking check against --against
115
119
 
@@ -290,12 +294,25 @@ Fmt options:
290
294
  --check Like --list, and exit 1 when any would: the CI gate
291
295
  -d, --diff Print a unified diff for each file whose form would
292
296
  change
297
+ --lint Report the style findings, key case and repeated
298
+ shapes, on standard error, and print nothing else
299
+ --strict With --lint, and exit 1 when there is a finding
293
300
 
294
301
  The fmt verb prints one document in the agreed form; with no file it
295
302
  reads standard input. Several files need one of the options above.
296
303
 
297
- Fmt exit codes: 0 formatted or clean, 1 a --check file would change,
298
- 2 usage, 4 a document does not parse.
304
+ Fmt exit codes: 0 formatted or clean, 1 a --check file would change or
305
+ a --strict finding, 2 usage, 4 a document does not parse.
306
+
307
+ The lsp verb runs the language server over standard input and output
308
+ (LSP: JSON-RPC with Content-Length framing) until the client exits;
309
+ editors launch it with no arguments. The standalone aontu-lsp binary
310
+ runs the same server.
311
+
312
+ The mcp verb runs the Model Context Protocol server over standard
313
+ input and output (newline-delimited JSON-RPC), confined below --root
314
+ when one is given. It is part of the npm build, as the standalone
315
+ aontu-mcp binary is.
299
316
 
300
317
  REPL commands:
301
318
  :help Show REPL help
@@ -1994,6 +2011,16 @@ function runMod(argv: string[]): number {
1994
2011
  return 2
1995
2012
  }
1996
2013
 
2014
+ // THE OLD LAYOUT IS NAMED, NOT READ. The lockfile and the vendored
2015
+ // closure moved under aontu_meta/; a project that still carries them
2016
+ // at its root would otherwise look untouched by any of these verbs,
2017
+ // which is the one silence worth breaking.
2018
+ if (existsSync(join(dir, 'aon_vendor')) || existsSync(join(dir, 'mod-lock.aon'))) {
2019
+ process.stderr.write(
2020
+ 'aontu: aon_vendor/ and mod-lock.aon now live under aontu_meta/: ' +
2021
+ 'move them, or run aontu mod tidy and aontu mod vendor\n')
2022
+ }
2023
+
1997
2024
  // `--against` gates a manifest and means nothing to the other two;
1998
2025
  // accepting it there would say it had been honoured.
1999
2026
  if (null != against && 'manifest' !== sub) {
@@ -3421,13 +3448,17 @@ function runAgentsMd(argv: string[]): number {
3421
3448
  // the form itself is the library's (ts/src/format.ts), and the two
3422
3449
  // ports agree on it row by row in test/spec/fmt.tsv.
3423
3450
 
3424
- const FMT_HELP = 'aontu fmt [-w|-l|--check|-d] <file>... (try --help)'
3451
+ const FMT_HELP = 'aontu fmt [-w|-l|--check|-d|--lint] <file>... (try --help)'
3425
3452
 
3426
- type FmtFlags = { write: boolean, list: boolean, check: boolean, diff: boolean }
3453
+ type FmtFlags = {
3454
+ write: boolean, list: boolean, check: boolean, diff: boolean, lint: boolean, strict: boolean,
3455
+ }
3427
3456
 
3428
3457
  function runFmt(argv: string[]): number | Promise<number> {
3429
3458
  const files: string[] = []
3430
- const flags: FmtFlags = { write: false, list: false, check: false, diff: false }
3459
+ const flags: FmtFlags = {
3460
+ write: false, list: false, check: false, diff: false, lint: false, strict: false,
3461
+ }
3431
3462
 
3432
3463
  for (const arg of argv) {
3433
3464
  if ('-h' === arg || '--help' === arg) {
@@ -3446,6 +3477,13 @@ function runFmt(argv: string[]): number | Promise<number> {
3446
3477
  else if ('-d' === arg || '--diff' === arg) {
3447
3478
  flags.diff = true
3448
3479
  }
3480
+ else if ('--lint' === arg) {
3481
+ flags.lint = true
3482
+ }
3483
+ else if ('--strict' === arg) {
3484
+ flags.lint = true
3485
+ flags.strict = true
3486
+ }
3449
3487
  else if (arg.startsWith('-')) {
3450
3488
  process.stderr.write(`aontu: unknown fmt option ${arg} (try --help)\n`)
3451
3489
  return 2
@@ -3477,7 +3515,7 @@ function runFmt(argv: string[]): number | Promise<number> {
3477
3515
  if (1 < files.length && !fmtQuiet(flags)) {
3478
3516
  process.stderr.write(
3479
3517
  `aontu: fmt prints one file; with ${files.length}, say --write, ` +
3480
- `--list, --check or --diff\n${FMT_HELP}\n`)
3518
+ `--list, --check, --diff or --lint\n${FMT_HELP}\n`)
3481
3519
  return 2
3482
3520
  }
3483
3521
 
@@ -3496,28 +3534,34 @@ function runFmt(argv: string[]): number | Promise<number> {
3496
3534
  return worst
3497
3535
  }
3498
3536
 
3499
- // An option that says what to do with a file that would change, in
3500
- // place of printing it.
3537
+ // An option that says what to do with a file, in place of printing
3538
+ // it: what to do when its form would change, or the lint.
3501
3539
  function fmtQuiet(flags: FmtFlags): boolean {
3502
- return flags.write || flags.list || flags.check || flags.diff
3540
+ return flags.write || flags.list || flags.check || flags.diff || flags.lint
3503
3541
  }
3504
3542
 
3505
3543
  // One document: 0 printed, clean or done; 1 a --check that would
3506
- // change; 2 a file that cannot be written; 4 a document that does not
3507
- // format, with the finding that says why.
3544
+ // change, or a --strict finding; 2 a file that cannot be written; 4 a
3545
+ // document that does not format, with the finding that says why. The
3546
+ // style findings go to standard error, one line each, in the shape
3547
+ // every linter prints: `file:line:col: rule: message`.
3508
3548
  function fmtOne(name: string, src: string, flags: FmtFlags): number {
3509
- const report = format(src, { path: name })
3549
+ const report = format(src, { path: name, lint: flags.lint })
3510
3550
  if ('error' === report.verdict) {
3511
3551
  process.stderr.write(`aontu: ${name} was not formatted\n` +
3512
3552
  report.errors.map(renderFinding).join('\n') + '\n')
3513
3553
  return 4
3514
3554
  }
3555
+ for (const f of report.findings) {
3556
+ process.stderr.write(`${name}:${f.line}:${f.col}: ${f.rule}: ${f.message}\n`)
3557
+ }
3558
+ const strict = flags.strict && 0 < report.findings.length ? 1 : 0
3515
3559
  if (!fmtQuiet(flags)) {
3516
3560
  process.stdout.write(report.text)
3517
3561
  return 0
3518
3562
  }
3519
3563
  if (!report.changed) {
3520
- return 0
3564
+ return strict
3521
3565
  }
3522
3566
  if (flags.list || flags.check) {
3523
3567
  process.stdout.write(name + '\n')
@@ -3534,7 +3578,49 @@ function fmtOne(name: string, src: string, flags: FmtFlags): number {
3534
3578
  return 2
3535
3579
  }
3536
3580
  }
3537
- return flags.check ? 1 : 0
3581
+ return flags.check ? 1 : strict
3582
+ }
3583
+
3584
+
3585
+ // ---------------------------------------------------------------------
3586
+ // The servers as verbs: `aontu lsp` runs the language server and
3587
+ // `aontu mcp` the MCP server over the CLI's own streams, so the one
3588
+ // command on PATH is the editor's and the agent's server too, and a
3589
+ // version manager (docs/design/ENV.0.md) has one thing to resolve. The
3590
+ // standalone bins (aontu-lsp, aontu-mcp) run the same functions. Each
3591
+ // server owns its exit; the CLI only dispatches. The pair is a
3592
+ // parameter of main so that a test can see the dispatch without a
3593
+ // server taking the test's own stdin.
3594
+
3595
+ type Servers = {
3596
+ lsp: () => void
3597
+ mcp: (argv: string[]) => void
3598
+ }
3599
+
3600
+ // The real pair takes the process's own stdin and stdout, which no
3601
+ // in-process test can lend it; the executable-entry tests in
3602
+ // cli.test.ts run each through a child process instead, so these two
3603
+ // lines are excluded from the in-process count, as the stdio wiring
3604
+ // of lsp-server.ts is.
3605
+ /* node:coverage ignore next 4 */
3606
+ const SERVERS: Servers = {
3607
+ lsp: () => void lspMain(),
3608
+ mcp: (argv) => void mcpMain(undefined, undefined, undefined, undefined, argv),
3609
+ }
3610
+
3611
+ // undefined: the server took the process; a number: an answer the CLI
3612
+ // gives itself, --help or a usage error.
3613
+ function runLsp(argv: string[], servers: Servers): number | undefined {
3614
+ for (const arg of argv) {
3615
+ if ('-h' === arg || '--help' === arg) {
3616
+ process.stdout.write(HELP)
3617
+ return 0
3618
+ }
3619
+ process.stderr.write(`aontu: lsp takes no arguments (try --help)\n`)
3620
+ return 2
3621
+ }
3622
+ servers.lsp()
3623
+ return undefined
3538
3624
  }
3539
3625
 
3540
3626
 
@@ -3562,7 +3648,7 @@ function parseTrustArg(value: string): TrustArg | undefined {
3562
3648
  }
3563
3649
 
3564
3650
 
3565
- function main(argv: string[]): void {
3651
+ function main(argv: string[], servers: Servers = SERVERS): void {
3566
3652
  // COLOUR OFF WHEN THE DESTINATION IS NOT A TERMINAL. Error frames
3567
3653
  // hardcoded their ANSI escapes, so a piped report and a `--jsonl`
3568
3654
  // answer carried terminal control codes into whatever read them (the
@@ -3612,6 +3698,13 @@ function main(argv: string[]): void {
3612
3698
  if ('fmt' === argv[2]) {
3613
3699
  return void Promise.resolve(runFmt(argv.slice(3))).then(finish)
3614
3700
  }
3701
+ if ('lsp' === argv[2]) {
3702
+ const code = runLsp(argv.slice(3), servers)
3703
+ return undefined === code ? undefined : finish(code)
3704
+ }
3705
+ if ('mcp' === argv[2]) {
3706
+ return servers.mcp(argv.slice(3))
3707
+ }
3615
3708
 
3616
3709
  if ('set' === argv[2]) {
3617
3710
  return finish(runSet(argv.slice(3)))