aontu 0.62.0 → 0.63.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 +1 -1
- package/dist/agentsmd.js +0 -27
- package/dist/agentsmd.js.map +1 -1
- package/dist/alias.js.map +1 -1
- package/dist/allow.js +0 -92
- package/dist/allow.js.map +1 -1
- package/dist/aontu.d.ts +1 -1
- package/dist/aontu.js +1 -79
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.d.ts +4 -0
- package/dist/aontumodel.js +33 -0
- package/dist/aontumodel.js.map +1 -0
- package/dist/cli.js +101 -620
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +0 -48
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js +0 -32
- package/dist/diff.js.map +1 -1
- package/dist/err.js +0 -40
- package/dist/err.js.map +1 -1
- package/dist/escape.js +0 -45
- package/dist/escape.js.map +1 -1
- package/dist/exactjson.d.ts +0 -35
- package/dist/exactjson.js +0 -131
- package/dist/exactjson.js.map +1 -1
- package/dist/format.js +13 -203
- package/dist/format.js.map +1 -1
- package/dist/grammar.d.ts +9 -0
- package/dist/grammar.js +54 -0
- package/dist/grammar.js.map +1 -0
- package/dist/graph.js +0 -26
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +0 -82
- package/dist/hcanon.js.map +1 -1
- package/dist/helpdoc.js +1 -1
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.d.ts +0 -6
- package/dist/hints.js +54 -55
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +0 -114
- package/dist/jsonschema.js.map +1 -1
- package/dist/keyorder.d.ts +0 -7
- package/dist/keyorder.js +0 -41
- package/dist/keyorder.js.map +1 -1
- package/dist/lang.js +17 -915
- package/dist/lang.js.map +1 -1
- package/dist/lower.js +11 -61
- package/dist/lower.js.map +1 -1
- package/dist/lsp-server.js +0 -16
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +12 -159
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +0 -26
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.js +0 -113
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +0 -130
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +0 -162
- package/dist/mod.js.map +1 -1
- package/dist/patch.js +0 -217
- package/dist/patch.js.map +1 -1
- package/dist/provenance.js +0 -140
- package/dist/provenance.js.map +1 -1
- package/dist/query.js +0 -75
- package/dist/query.js.map +1 -1
- package/dist/reach.js +0 -43
- package/dist/reach.js.map +1 -1
- package/dist/relation.js +0 -61
- package/dist/relation.js.map +1 -1
- package/dist/render.js +20 -135
- package/dist/render.js.map +1 -1
- package/dist/report-sarif.d.ts +0 -11
- package/dist/report-sarif.js +0 -28
- package/dist/report-sarif.js.map +1 -1
- package/dist/sig.js +0 -35
- package/dist/sig.js.map +1 -1
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/siggate.js +0 -4
- package/dist/siggate.js.map +1 -1
- package/dist/site.js +3 -29
- package/dist/site.js.map +1 -1
- package/dist/subsume.d.ts +0 -10
- package/dist/subsume.js +0 -137
- package/dist/subsume.js.map +1 -1
- package/dist/template.d.ts +2 -1
- package/dist/template.js +58 -138
- package/dist/template.js.map +1 -1
- package/dist/trim.js +0 -41
- package/dist/trim.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.js +12 -242
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +0 -22
- package/dist/utility.js.map +1 -1
- package/dist/val/AbnfFuncVal.d.ts +18 -0
- package/dist/val/AbnfFuncVal.js +132 -0
- package/dist/val/AbnfFuncVal.js.map +1 -0
- package/dist/val/AbsentVal.d.ts +11 -0
- package/dist/val/AbsentVal.js +30 -0
- package/dist/val/AbsentVal.js.map +1 -0
- package/dist/val/AggFuncVal.d.ts +10 -1
- package/dist/val/AggFuncVal.js +104 -116
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/ArithFuncVal.js +0 -12
- package/dist/val/ArithFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -78
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.js +0 -16
- package/dist/val/BigDecimalVal.js.map +1 -1
- package/dist/val/BigIntegerVal.js +0 -16
- package/dist/val/BigIntegerVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +0 -9
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/CmpFuncVal.js +2 -59
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +0 -29
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +0 -500
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.js +0 -2
- package/dist/val/ContainerKindVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +0 -3
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.js +0 -179
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +0 -152
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.js +0 -3
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +1 -1
- package/dist/val/EmitFuncVal.js +6 -119
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/ExpectVal.js +0 -62
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +0 -25
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +7 -127
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.js +0 -15
- package/dist/val/GraphAtomVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +0 -13
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.js +0 -61
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +0 -20
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -46
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +0 -57
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +0 -11
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +0 -151
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.js +0 -27
- package/dist/val/MatchFuncVal.js.map +1 -1
- package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
- package/dist/val/MaybeFuncVal.js +50 -0
- package/dist/val/MaybeFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js +0 -18
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +0 -60
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +8 -42
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/NumberVal.js +0 -15
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -0
- package/dist/val/OpBaseVal.js +3 -15
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/PackFuncVal.js +0 -34
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +0 -6
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.js +0 -41
- package/dist/val/PathVal.js.map +1 -1
- package/dist/val/PlaceVal.js +0 -25
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +1 -7
- package/dist/val/PlusOpVal.js +13 -74
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +0 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.js +0 -167
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.js +0 -55
- package/dist/val/RecurseVal.js.map +1 -1
- package/dist/val/RefVal.js +0 -282
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +0 -232
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.js +0 -49
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.js +0 -11
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StrFuncVal.js +0 -18
- package/dist/val/StrFuncVal.js.map +1 -1
- package/dist/val/SuperFuncVal.js +0 -32
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +0 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TranslateFuncVal.js +1 -3
- package/dist/val/TranslateFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +0 -11
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -0
- package/dist/val/Val.js +2 -133
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +0 -12
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.js +0 -37
- package/dist/val/arith.js.map +1 -1
- package/dist/val/caserange.js +0 -61
- package/dist/val/caserange.js.map +1 -1
- package/dist/val/members.js +0 -6
- package/dist/val/members.js.map +1 -1
- package/dist/val/numcmp.js +0 -11
- package/dist/val/numcmp.js.map +1 -1
- package/dist/val/numkind.js +0 -145
- package/dist/val/numkind.js.map +1 -1
- package/dist/val/valutil.js +0 -16
- package/dist/val/valutil.js.map +1 -1
- package/dist/vet.js +0 -461
- package/dist/vet.js.map +1 -1
- package/dist/view.js +0 -414
- package/dist/view.js.map +1 -1
- package/dist/walk.js +0 -41
- package/dist/walk.js.map +1 -1
- package/grammar/aontu.abnf +8 -6
- package/grammar/aontu.gbnf +4 -4
- package/grammar/aontu.lark +4 -4
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +4 -2
- package/src/agentsmd.ts +0 -35
- package/src/alias.ts +0 -39
- package/src/allow.ts +1 -96
- package/src/aontu.ts +3 -110
- package/src/aontumodel.ts +32 -0
- package/src/cli.ts +113 -682
- package/src/ctx.ts +0 -103
- package/src/diff.ts +0 -40
- package/src/err.ts +0 -40
- package/src/escape.ts +0 -46
- package/src/exactjson.ts +0 -131
- package/src/format.ts +14 -257
- package/src/grammar.ts +72 -0
- package/src/graph.ts +0 -61
- package/src/hcanon.ts +0 -82
- package/src/helpdoc.ts +1 -1
- package/src/hints.ts +66 -57
- package/src/jsonschema.ts +0 -123
- package/src/keyorder.ts +0 -42
- package/src/lang.ts +19 -931
- package/src/lower.ts +12 -62
- package/src/lsp-server.ts +0 -16
- package/src/lsp.ts +12 -180
- package/src/mcp-server.ts +0 -31
- package/src/mcp.ts +0 -130
- package/src/mod-tool.ts +0 -158
- package/src/mod.ts +0 -178
- package/src/patch.ts +0 -232
- package/src/provenance.ts +0 -183
- package/src/query.ts +0 -84
- package/src/reach.ts +0 -53
- package/src/relation.ts +0 -84
- package/src/render.ts +20 -172
- package/src/report-sarif.ts +0 -48
- package/src/sig.ts +0 -35
- package/src/sigdecl.ts +1 -1
- package/src/siggate.ts +0 -30
- package/src/site.ts +3 -29
- package/src/subsume.ts +1 -161
- package/src/template.ts +69 -140
- package/src/trim.ts +0 -53
- package/src/type.ts +2 -45
- package/src/unify.ts +13 -251
- package/src/utility.ts +0 -31
- package/src/val/AbnfFuncVal.ts +181 -0
- package/src/val/AbsentVal.ts +54 -0
- package/src/val/AggFuncVal.ts +152 -188
- package/src/val/ArithFuncVal.ts +0 -20
- package/src/val/BagVal.ts +1 -78
- package/src/val/BigDecimalVal.ts +0 -16
- package/src/val/BigIntegerVal.ts +0 -16
- package/src/val/CloseFuncVal.ts +0 -9
- package/src/val/CmpFuncVal.ts +4 -166
- package/src/val/ConjunctVal.ts +0 -33
- package/src/val/ConstraintVal.ts +2 -537
- package/src/val/ContainerKindVal.ts +0 -18
- package/src/val/CopyFuncVal.ts +0 -5
- package/src/val/Decimal.ts +1 -185
- package/src/val/DeprecateFuncVal.ts +0 -10
- package/src/val/DisjunctVal.ts +0 -157
- package/src/val/EachFuncVal.ts +0 -40
- package/src/val/EmitFuncVal.ts +8 -208
- package/src/val/ExpectVal.ts +0 -62
- package/src/val/FilterFuncVal.ts +0 -55
- package/src/val/FuncBaseVal.ts +9 -130
- package/src/val/GraphAtomVal.ts +0 -42
- package/src/val/HideFuncVal.ts +0 -15
- package/src/val/IntegerVal.ts +0 -61
- package/src/val/JunctionVal.ts +0 -20
- package/src/val/KeyFuncVal.ts +0 -48
- package/src/val/ListVal.ts +0 -59
- package/src/val/LowerFuncVal.ts +0 -12
- package/src/val/MapVal.ts +0 -151
- package/src/val/MatchFuncVal.ts +0 -59
- package/src/val/MaybeFuncVal.ts +86 -0
- package/src/val/MoveFuncVal.ts +0 -20
- package/src/val/NilVal.ts +0 -60
- package/src/val/NomFuncVal.ts +8 -95
- package/src/val/NumberVal.ts +0 -16
- package/src/val/OpBaseVal.ts +4 -17
- package/src/val/PackFuncVal.ts +0 -63
- package/src/val/PathFuncVal.ts +0 -32
- package/src/val/PathVal.ts +0 -66
- package/src/val/PlaceVal.ts +0 -45
- package/src/val/PlusOpVal.ts +18 -75
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +0 -179
- package/src/val/RecurseVal.ts +0 -81
- package/src/val/RefVal.ts +1 -285
- package/src/val/ReferFuncVal.ts +0 -255
- package/src/val/ScalarKindVal.ts +0 -50
- package/src/val/ScalarVal.ts +0 -12
- package/src/val/StrFuncVal.ts +0 -44
- package/src/val/SuperFuncVal.ts +0 -42
- package/src/val/TopVal.ts +0 -1
- package/src/val/TranslateFuncVal.ts +1 -51
- package/src/val/UpperFuncVal.ts +0 -12
- package/src/val/Val.ts +3 -192
- package/src/val/VarVal.ts +0 -15
- package/src/val/arith.ts +0 -92
- package/src/val/caserange.ts +0 -62
- package/src/val/members.ts +0 -23
- package/src/val/numcmp.ts +1 -27
- package/src/val/numkind.ts +0 -149
- package/src/val/valutil.ts +0 -16
- package/src/vet.ts +1 -582
- package/src/view.ts +0 -507
- package/src/walk.ts +0 -41
- package/dist/std.d.ts +0 -3
- package/dist/std.js +0 -672
- package/dist/std.js.map +0 -1
- package/dist/val/FormFuncVal.js +0 -55
- package/dist/val/FormFuncVal.js.map +0 -1
- package/dist/val/NamerFuncVal.d.ts +0 -12
- package/dist/val/NamerFuncVal.js +0 -176
- package/dist/val/NamerFuncVal.js.map +0 -1
- package/src/std.ts +0 -683
package/src/cli.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// Command-line interface for Aontu.
|
|
4
|
-
//
|
|
5
|
-
// aontu [options] [file]
|
|
6
|
-
//
|
|
7
|
-
// With a file argument, the file is evaluated and the result printed.
|
|
8
|
-
// With no file on an interactive terminal, a REPL is started. With no
|
|
9
|
-
// file and piped input, the source is read from stdin. See HELP below.
|
|
10
3
|
|
|
11
4
|
// Named imports, not `import * as`: the namespace form makes tsc emit the
|
|
12
5
|
import { evalFailure } from './query'
|
|
@@ -29,7 +22,10 @@ import {
|
|
|
29
22
|
} from './aontu'
|
|
30
23
|
import type { AllowDecision, AllowReport, AllowVerdict } from './allow'
|
|
31
24
|
import type { RenderCoverage, RenderReport } from './render'
|
|
32
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
desugarTemplate, resugarTemplate, templateOutputs, markerFor,
|
|
27
|
+
markerFromProfiles,
|
|
28
|
+
} from './template'
|
|
33
29
|
import { outsideRoot } from './mcp'
|
|
34
30
|
import { sarifReport } from './report-sarif'
|
|
35
31
|
import { main as lspMain } from './lsp-server'
|
|
@@ -90,7 +86,8 @@ const HELP = `Usage: aontu [options] [file]
|
|
|
90
86
|
aontu render [--at <path>] [--profile <file>]... [--unit <path>]
|
|
91
87
|
[--stdout | --out <dir> | --check <dir> | --coverage]
|
|
92
88
|
[--coverage-at <path>] [--strict] <file>
|
|
93
|
-
aontu template [--resugar] [--check] [--marker <token>]
|
|
89
|
+
aontu template [--resugar] [--check] [--marker <token>]
|
|
90
|
+
[--profile <file>] <file>
|
|
94
91
|
aontu hash [options] <file>
|
|
95
92
|
aontu mod tidy|verify|vendor|manifest [options] [dir]
|
|
96
93
|
aontu get <path> [options] <file>
|
|
@@ -98,7 +95,8 @@ const HELP = `Usage: aontu [options] [file]
|
|
|
98
95
|
aontu set <path>=<value>... --entry <file> --overlay <file>
|
|
99
96
|
aontu allow --role <role> [--at <path>] <roles-file> <path>...
|
|
100
97
|
aontu agentsmd [--write <AGENTS.md>] [--depth <n>] <file>
|
|
101
|
-
aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
|
|
98
|
+
aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
|
|
99
|
+
[--profile <file>] <file>...
|
|
102
100
|
aontu help [topic] [--format text|json]
|
|
103
101
|
aontu explain <code> | --list [--format text|json]
|
|
104
102
|
aontu init [dir]
|
|
@@ -338,8 +336,9 @@ up on its own, or a relation, root or path that names nothing.
|
|
|
338
336
|
Render options:
|
|
339
337
|
--at <path> Render the value at this path ($.a.b); the root by
|
|
340
338
|
default
|
|
341
|
-
--profile <file> A profile document,
|
|
342
|
-
against aontu:
|
|
339
|
+
--profile <file> A profile document, aontu: render: Lang: {lang,
|
|
340
|
+
...}, vetted against aontu:render; repeatable,
|
|
341
|
+
one per language
|
|
343
342
|
--unit <path> Render only the unit with this path
|
|
344
343
|
--stdout One unit's bytes and nothing else (with --unit when
|
|
345
344
|
the instance has several)
|
|
@@ -363,8 +362,8 @@ does not stand up or the instance is not aontu:code.
|
|
|
363
362
|
A render entry file whose extension is not .aon is a TEMPLATE: a
|
|
364
363
|
generator in the target's own syntax, whose marker lines carry aontu
|
|
365
364
|
and whose other lines are output. It is desugared before it is
|
|
366
|
-
evaluated, and
|
|
367
|
-
|
|
365
|
+
evaluated, and a language the table does not know names its marker with
|
|
366
|
+
--marker, or declares it once in a profile file that --profile reads.
|
|
368
367
|
|
|
369
368
|
Template options:
|
|
370
369
|
--resugar The file is the canonical aontu; print the template
|
|
@@ -372,7 +371,9 @@ Template options:
|
|
|
372
371
|
--check Desugar and resugar, and exit 1 if the file is not
|
|
373
372
|
what the round trip answers
|
|
374
373
|
--marker <t> The marker, when the extension does not name it
|
|
375
|
-
(default //-, and #- --- /*- by extension)
|
|
374
|
+
(default //-, and #- --- /*- <!--- by extension)
|
|
375
|
+
--profile <f> A profile file, whose template.ext names the
|
|
376
|
+
extensions it marks and template.marker the marker
|
|
376
377
|
|
|
377
378
|
The template verb prints the canonical aontu form of a generator
|
|
378
379
|
written in the target's own syntax: a marked line is aontu source, and
|
|
@@ -465,7 +466,9 @@ Fmt options:
|
|
|
465
466
|
shapes, on standard error, and print nothing else
|
|
466
467
|
--strict With --lint, and exit 1 when there is a finding
|
|
467
468
|
--marker <t> The file is a generator, and this is its marker
|
|
468
|
-
(default //-, and #- --- /*- by extension)
|
|
469
|
+
(default //-, and #- --- /*- <!--- by extension)
|
|
470
|
+
--profile <f> A profile file, whose template.ext names the
|
|
471
|
+
extensions it marks and template.marker the marker
|
|
469
472
|
|
|
470
473
|
The fmt verb prints one document in the agreed form; with no file it
|
|
471
474
|
reads standard input. Several files need one of the options above.
|
|
@@ -512,32 +515,9 @@ function version(): string {
|
|
|
512
515
|
}
|
|
513
516
|
|
|
514
517
|
|
|
515
|
-
// The terminal colour escapes the parser puts in its message text. A
|
|
516
|
-
// machine-readable report is no place for them, which is the rule
|
|
517
|
-
// findingOf states in ts/src/vet.ts; the twin here rather than an
|
|
518
|
-
// import because go/cmd/aontu carries its own for the same reason (the
|
|
519
|
-
// engine's is not exported to its command).
|
|
520
518
|
const EVAL_ANSI = new RegExp('\u001b\\[[0-9;]*m', 'g')
|
|
521
519
|
|
|
522
520
|
|
|
523
|
-
// THE ENGINE'S DIAGNOSIS AS A FINDING (G11 phase 7). The bare command
|
|
524
|
-
// was the one verb whose failure had no machine-readable form, so the
|
|
525
|
-
// default entry point was the one an agent had to parse with a regular
|
|
526
|
-
// expression.
|
|
527
|
-
//
|
|
528
|
-
// THE HEADLINE ONLY, and no `hint`. Both are parity decisions rather
|
|
529
|
-
// than economies: the frames under the headline are drawn for a person
|
|
530
|
-
// reading a terminal and only the first line is held to byte parity
|
|
531
|
-
// between the ports (the rule findingOf states), and the hint TABLES
|
|
532
|
-
// are deliberately not in parity while the code registry is -- so a
|
|
533
|
-
// hint here would make the two ports answer differently for a code
|
|
534
|
-
// only one of them explains. `aontu explain <code>` is where the hint
|
|
535
|
-
// lives, which is what phase 3 built it for.
|
|
536
|
-
//
|
|
537
|
-
// The CLASS comes from the registry rather than from the nil, because
|
|
538
|
-
// the registry is what both ports hold set-equal
|
|
539
|
-
// (test/spec/errcodes.tsv). Mirrors evalFinding in
|
|
540
|
-
// go/cmd/aontu/main.go.
|
|
541
521
|
function evalFinding(code: string, text: string): VetFinding {
|
|
542
522
|
return {
|
|
543
523
|
class: codeClass(code),
|
|
@@ -545,10 +525,6 @@ function evalFinding(code: string, text: string): VetFinding {
|
|
|
545
525
|
message: text.split('\n')[0].replace(EVAL_ANSI, ''),
|
|
546
526
|
path: '$',
|
|
547
527
|
severity: 'error',
|
|
548
|
-
// NO SITE. The bare command's failure is the whole document not
|
|
549
|
-
// standing up, and the two sites a conflict names are in the
|
|
550
|
-
// frames the text form prints; naming one of them here would be a
|
|
551
|
-
// choice the engine has not made.
|
|
552
528
|
sites: [],
|
|
553
529
|
}
|
|
554
530
|
}
|
|
@@ -562,12 +538,6 @@ function evalSource(
|
|
|
562
538
|
mode: Mode,
|
|
563
539
|
): { ok: boolean; text: string; findings: VetFinding[] } {
|
|
564
540
|
try {
|
|
565
|
-
// exactJSON, not JSON.stringify: a document using the `0d` exact
|
|
566
|
-
// leaves generates bigints and Decimals, which JSON.stringify cannot
|
|
567
|
-
// write (D9). The CLI prints INDENTED JSON and the shared suite's
|
|
568
|
-
// `gens` mode prints COMPACT JSON, but both go through this one
|
|
569
|
-
// emitter -- an indent argument rather than a second implementation,
|
|
570
|
-
// so the two cannot drift from each other or from the Go port.
|
|
571
541
|
const text = 'canon' === mode
|
|
572
542
|
? aontu.unify(src).canon
|
|
573
543
|
: exactJSON(aontu.generate(src), 2)
|
|
@@ -577,13 +547,6 @@ function evalSource(
|
|
|
577
547
|
const msg = (err instanceof AontuError || true === err?.aontu)
|
|
578
548
|
? err.message
|
|
579
549
|
: String(err?.message ?? err)
|
|
580
|
-
// WHAT THE ENGINE COLLECTED, when it collected anything: an
|
|
581
|
-
// AontuError carries the NilVals the run failed on, already
|
|
582
|
-
// materialised (handleErrors in ts/src/aontu.ts), and their first
|
|
583
|
-
// is the diagnosis every other verb reports. An error raised
|
|
584
|
-
// outside the engine's own collection -- exactJSON's circular
|
|
585
|
-
// refusal, a foreign object claiming to be one -- carries none,
|
|
586
|
-
// and answers with the text alone rather than an invented code.
|
|
587
550
|
const errs: any[] = 'function' === typeof err?.errs ? err.errs() : []
|
|
588
551
|
const first: any = errs[0]
|
|
589
552
|
return {
|
|
@@ -619,23 +582,11 @@ function emitEval(
|
|
|
619
582
|
}
|
|
620
583
|
|
|
621
584
|
|
|
622
|
-
// The include capability the main verb runs with (G5, docs/trust.md).
|
|
623
|
-
// `--trust` and `--include-root` set it explicitly; the default is
|
|
624
|
-
// 'system' WITH the warning window: every resolution that escapes the
|
|
625
|
-
// entry root or goes through package resolution prints a one-line
|
|
626
|
-
// stderr warning naming the flag a future default will require
|
|
627
|
-
// (phase 6, the staged flip).
|
|
628
585
|
type TrustArg = (
|
|
629
586
|
| { kind: 'system-warn' }
|
|
630
587
|
| { kind: 'system' }
|
|
631
588
|
| { kind: 'none' }
|
|
632
589
|
| { kind: 'root', dir?: string }
|
|
633
|
-
// EXTENSIONS READ AS TEXT ride with the capability rather than
|
|
634
|
-
// beside it: both answer "what may an include read", both are
|
|
635
|
-
// stripped by takeTrust before a verb parses its own tail, and a
|
|
636
|
-
// verb that threads one and not the other is the G5 defect again --
|
|
637
|
-
// `aontu vet` running under a flag the bare command honoured and it
|
|
638
|
-
// did not.
|
|
639
590
|
) & { textExt: string[] }
|
|
640
591
|
|
|
641
592
|
|
|
@@ -679,16 +630,6 @@ function trustOpts(trust: TrustArg, entryRoot: string): any {
|
|
|
679
630
|
}
|
|
680
631
|
|
|
681
632
|
|
|
682
|
-
// EVERY VERB honours the include capability, not just the bare
|
|
683
|
-
// command. G5 wired `--trust`/`--include-root` to `aontu <file>` alone,
|
|
684
|
-
// so `aontu vet schema.aon data.json` -- the surface an agent actually
|
|
685
|
-
// scripts -- ran the full system resolver with no flag to confine it
|
|
686
|
-
// and no warning (use-cases/REVIEW.md finding G). The flags are
|
|
687
|
-
// stripped here, before each verb parses its own tail, so a verb only
|
|
688
|
-
// has to pass the profile on to its engine.
|
|
689
|
-
//
|
|
690
|
-
// Returns undefined when the spelling is wrong, with the message
|
|
691
|
-
// already printed: the caller answers the usage class.
|
|
692
633
|
function takeTrust(argv: string[]):
|
|
693
634
|
{ argv: string[], trust: TrustArg } | undefined {
|
|
694
635
|
const rest: string[] = []
|
|
@@ -799,17 +740,6 @@ function runFile(
|
|
|
799
740
|
src = readFileSync(file, 'utf8')
|
|
800
741
|
}
|
|
801
742
|
catch (err: any) {
|
|
802
|
-
// A MISTYPED VERB READS AS A FILE NAME, and until G11 phase 2 that
|
|
803
|
-
// was only said when there were TWO of them. The one-argument case
|
|
804
|
-
// is the one an agent actually produces -- `aontu help`, `aontu
|
|
805
|
-
// init`, `aontu ontology` -- and it answered `cannot read help:
|
|
806
|
-
// ...`, which describes the symptom and hides the cause.
|
|
807
|
-
//
|
|
808
|
-
// The test is SHAPE, not existence: a bare word (no separator, no
|
|
809
|
-
// extension) that cannot be read was meant as a verb, while
|
|
810
|
-
// `./help`, `help.aon` and `/tmp/help` were meant as paths and keep
|
|
811
|
-
// the file diagnosis and its exit 1. That is the same escape hatch
|
|
812
|
-
// the subcommand dispatch documents. Mirrors go/cmd/aontu/main.go.
|
|
813
743
|
if (looksLikeVerb(file)) {
|
|
814
744
|
process.stderr.write(
|
|
815
745
|
`aontu: \`${file}\` is not a file, and not a verb this port knows\n`)
|
|
@@ -826,12 +756,6 @@ function runFile(
|
|
|
826
756
|
}
|
|
827
757
|
|
|
828
758
|
const path = resolve(file)
|
|
829
|
-
// `fs` IS WHAT MAKES A FRAME EXCERPT THE FILE IT NAMES. Without it,
|
|
830
|
-
// err.ts's resolveSrc falls back to the ENTRY text, so a frame whose
|
|
831
|
-
// arrow says `lib/types.aon:2:6` printed the entry's line 2 under it
|
|
832
|
-
// -- a real file name over another file's line, which
|
|
833
|
-
// docs/reference-api.md forbids in the same words it uses to require
|
|
834
|
-
// the name.
|
|
835
759
|
const aontu = new Aontu({
|
|
836
760
|
path,
|
|
837
761
|
errfs: { existsSync, readFileSync },
|
|
@@ -856,15 +780,6 @@ function runStdin(
|
|
|
856
780
|
}
|
|
857
781
|
|
|
858
782
|
|
|
859
|
-
// THE REPL AS AN INSPECTION TOOL (G7 phase 7): `:load` holds a
|
|
860
|
-
// document, and `:get`, `:keys` and `:why` ask the query and
|
|
861
|
-
// provenance surfaces about it, so the session is a place to
|
|
862
|
-
// INTERROGATE a definition rather than only to evaluate snippets.
|
|
863
|
-
//
|
|
864
|
-
// The command handler is a PURE FUNCTION of (state, line): a readline
|
|
865
|
-
// loop is untestable, and every answer this REPL gives has to be as
|
|
866
|
-
// checkable as the CLI's. File reading is injected for the same
|
|
867
|
-
// reason.
|
|
868
783
|
export type ReplState = {
|
|
869
784
|
// How a value renders: the `:canon` / `:json` toggle.
|
|
870
785
|
mode: Mode
|
|
@@ -873,11 +788,6 @@ export type ReplState = {
|
|
|
873
788
|
jsonl: boolean
|
|
874
789
|
name?: string
|
|
875
790
|
src?: string
|
|
876
|
-
// The include capability the session evaluates under. `--trust` and
|
|
877
|
-
// `--include-root` were parsed and then DROPPED on the way to the
|
|
878
|
-
// REPL, so `--jsonl` -- the surface built to be driven by a harness
|
|
879
|
-
// -- ran unconfined however it was invoked (use-cases/REVIEW.md
|
|
880
|
-
// finding G). The state carries it, so every line honours it.
|
|
881
791
|
trust?: TrustArg
|
|
882
792
|
}
|
|
883
793
|
|
|
@@ -1032,14 +942,6 @@ function runRepl(initialMode: Mode, jsonl: boolean, trust: TrustArg): void {
|
|
|
1032
942
|
})
|
|
1033
943
|
|
|
1034
944
|
rl.on('close', () => {
|
|
1035
|
-
// The closing newline is for a HUMAN, so it is written only for
|
|
1036
|
-
// one: it moves the terminal off the prompt line that `rl` left
|
|
1037
|
-
// hanging. In `--jsonl` there is no prompt, every answer already
|
|
1038
|
-
// ends in its own newline, and this one appended a bare empty line
|
|
1039
|
-
// to the stream -- a record that is not JSON, at the end of a
|
|
1040
|
-
// protocol whose whole contract is one JSON object per line. A
|
|
1041
|
-
// harness parsing every line it receives failed on it, after the
|
|
1042
|
-
// commands had all succeeded. Mirrors go/cmd/aontu/repl.go.
|
|
1043
945
|
if (!jsonl) {
|
|
1044
946
|
process.stdout.write('\n')
|
|
1045
947
|
}
|
|
@@ -1051,16 +953,6 @@ function runRepl(initialMode: Mode, jsonl: boolean, trust: TrustArg): void {
|
|
|
1051
953
|
}
|
|
1052
954
|
|
|
1053
955
|
|
|
1054
|
-
|
|
1055
|
-
// THE VET VERB (G2 phase 3).
|
|
1056
|
-
//
|
|
1057
|
-
// Exit codes are VERDICT CLASSES, not a pass/fail bit: an agent loop
|
|
1058
|
-
// branches on "the data contradicts the truth" (1) differently from
|
|
1059
|
-
// "the data has not supplied everything the truth requires" (3), and
|
|
1060
|
-
// differently again from "the schema itself is broken" (4), which is
|
|
1061
|
-
// never the data's fault. 2 stays what it already was for this CLI --
|
|
1062
|
-
// the caller got the invocation wrong -- which is why an unreadable
|
|
1063
|
-
// file is a 2 rather than a 4.
|
|
1064
956
|
const VET_EXIT: Record<VetVerdict, number> = {
|
|
1065
957
|
valid: 0,
|
|
1066
958
|
invalid: 1,
|
|
@@ -1130,14 +1022,6 @@ function parseVetArgs(argv: string[]): { args?: VetArgs; err?: string } {
|
|
|
1130
1022
|
format = f
|
|
1131
1023
|
}
|
|
1132
1024
|
else if ('--max-errors' === arg) {
|
|
1133
|
-
// ONE GRAMMAR, spelled the same way in both ports: decimal
|
|
1134
|
-
// digits, one to nine of them, at least 1. `Number()` alone
|
|
1135
|
-
// accepted `1.0`, `1e2`, `0x10` and ` 3`, which Go's parser
|
|
1136
|
-
// refuses -- so the same documented invocation meant different
|
|
1137
|
-
// things in the two shipped commands. The nine-digit ceiling is
|
|
1138
|
-
// where the ports would part company again: beyond it Go's
|
|
1139
|
-
// integer conversion saturates, and a cap nobody can reach is
|
|
1140
|
-
// not worth a divergence.
|
|
1141
1025
|
const raw = argv[++i]
|
|
1142
1026
|
if (!/^[0-9]{1,9}$/.test(raw ?? '') || 1 > Number(raw)) {
|
|
1143
1027
|
return { err: 'aontu: --max-errors needs a positive whole number' }
|
|
@@ -1220,10 +1104,6 @@ function renderFinding(f: VetFinding): string {
|
|
|
1220
1104
|
out.push(` actual: ${f.actual}`)
|
|
1221
1105
|
}
|
|
1222
1106
|
for (const s of f.sites) {
|
|
1223
|
-
// Every site carries the canon of the value it stands for: that is
|
|
1224
|
-
// what makes the two sides of a conflict readable side by side. A
|
|
1225
|
-
// site's file is always a string -- empty when the value belongs to
|
|
1226
|
-
// neither document -- so there is nothing to coalesce here.
|
|
1227
1107
|
out.push(` ${s.role}: ${s.file}:${s.row}:${s.col} (${s.value})`)
|
|
1228
1108
|
}
|
|
1229
1109
|
|
|
@@ -1333,18 +1213,9 @@ function vetOnce(args: VetArgs, trust: TrustArg): number {
|
|
|
1333
1213
|
return 2
|
|
1334
1214
|
}
|
|
1335
1215
|
|
|
1336
|
-
// Each data file is vetted on its own, because a parsed tree is
|
|
1337
|
-
// single-use (docs/reference-api.md) -- and because two data files
|
|
1338
|
-
// are two candidates for the same truth, not one merged candidate.
|
|
1339
1216
|
let verdict: VetVerdict = 'valid'
|
|
1340
1217
|
let truncated = false
|
|
1341
1218
|
const findings: VetFinding[] = []
|
|
1342
|
-
// COVERAGE ACROSS SEVERAL DATA FILES (G11 phase 5). Two data files
|
|
1343
|
-
// are two candidates for one truth, so the schema side is the SAME
|
|
1344
|
-
// for each: `declared` is taken once, and a declaration is unused
|
|
1345
|
-
// only when NO file met it -- the intersection, because a
|
|
1346
|
-
// declaration one file exercised is exercised. The data side adds
|
|
1347
|
-
// up: leaves and checked leaves sum, and `unchecked` is the union.
|
|
1348
1219
|
let cov: VetCoverage | undefined
|
|
1349
1220
|
// Initialised rather than left undefined: it is filled in the same
|
|
1350
1221
|
// block that sets `cov`, so a fallback at the read below would be an
|
|
@@ -1363,12 +1234,6 @@ function vetOnce(args: VetArgs, trust: TrustArg): number {
|
|
|
1363
1234
|
maxErrors: args.maxErrors,
|
|
1364
1235
|
schemaUrl: args.schema,
|
|
1365
1236
|
dataUrl: source.file,
|
|
1366
|
-
// The paths as well as the labels: a relative `@"file"` load
|
|
1367
|
-
// inside either document resolves from ITS OWN directory, the
|
|
1368
|
-
// way `aontu <file>` already resolves one (runFile above). The
|
|
1369
|
-
// path is passed AS TYPED, not resolved: it doubles as the
|
|
1370
|
-
// label above, and a report that mixed the typed path with an
|
|
1371
|
-
// absolute one would name the same file two ways.
|
|
1372
1237
|
schemaPath: args.schema,
|
|
1373
1238
|
dataPath: source.file,
|
|
1374
1239
|
coverage: args.coverage,
|
|
@@ -1400,27 +1265,11 @@ function vetOnce(args: VetArgs, trust: TrustArg): number {
|
|
|
1400
1265
|
unusedSeen = true
|
|
1401
1266
|
}
|
|
1402
1267
|
|
|
1403
|
-
// A SCHEMA-SIDE FAULT IS THE SAME FAULT FOR EVERY DATA FILE, so it
|
|
1404
|
-
// is reported ONCE. `error` means exactly that -- the run could not
|
|
1405
|
-
// be set up from the truth's side, never the data's (the exit table
|
|
1406
|
-
// in docs/reference-api.md) -- so the report the first file
|
|
1407
|
-
// produced is the report every later file would produce, character
|
|
1408
|
-
// for character. Concatenating them repeated one broken schema N
|
|
1409
|
-
// times and, past the cap, marked the report `truncated` over a
|
|
1410
|
-
// single underlying fault. It only became visible once the `error`
|
|
1411
|
-
// verdict started carrying findings at all: while the list was
|
|
1412
|
-
// empty there was nothing to duplicate.
|
|
1413
1268
|
if ('error' === report.verdict) {
|
|
1414
1269
|
break
|
|
1415
1270
|
}
|
|
1416
1271
|
}
|
|
1417
1272
|
|
|
1418
|
-
// The cap is on the REPORT, not on each file. Capping every file's
|
|
1419
|
-
// list and then concatenating them let `--max-errors 1` emit one
|
|
1420
|
-
// finding PER FILE -- and leave `truncated` false while doing it,
|
|
1421
|
-
// because no single file had been cut. The engine still caps each
|
|
1422
|
-
// run, so a pathological file cannot flood the aggregate before it
|
|
1423
|
-
// gets here; this is the second, honest cut.
|
|
1424
1273
|
const cap = args.maxErrors ?? VET_MAX_ERRORS
|
|
1425
1274
|
const kept = cap < findings.length ? findings.slice(0, cap) : findings
|
|
1426
1275
|
|
|
@@ -1442,12 +1291,6 @@ function vetOnce(args: VetArgs, trust: TrustArg): number {
|
|
|
1442
1291
|
|
|
1443
1292
|
process.stdout.write(text + '\n')
|
|
1444
1293
|
|
|
1445
|
-
// A VACUOUS CHECK IS A FAILED GATE UNDER `--strict-coverage`, and
|
|
1446
|
-
// only under it: the verdict WORD is unchanged, so nothing that
|
|
1447
|
-
// passes today starts failing, and a caller who wants the stronger
|
|
1448
|
-
// gate asks for it. The reason goes to stderr, because stdout is a
|
|
1449
|
-
// report contract -- a JSON consumer reads `coverage.vacuous` and a
|
|
1450
|
-
// person reads this.
|
|
1451
1294
|
if (true === args.strictCoverage && true === report.coverage?.vacuous) {
|
|
1452
1295
|
process.stderr.write(
|
|
1453
1296
|
'aontu: no data leaf was constrained by the schema:' +
|
|
@@ -1483,19 +1326,6 @@ function sleep(ms: number): Promise<void> {
|
|
|
1483
1326
|
}
|
|
1484
1327
|
|
|
1485
1328
|
|
|
1486
|
-
// Resolve true when any watched file's signature moves off `before`.
|
|
1487
|
-
// This is the real waiter: it never resolves false, so a real watch
|
|
1488
|
-
// runs until the process is interrupted; tests inject their own waiter
|
|
1489
|
-
// to bound the loop, and pass a short pollMs when they drive this one
|
|
1490
|
-
// directly. The interval is a required argument (the command passes
|
|
1491
|
-
// WATCH_POLL_MS) so there is no defaulting branch a test could never
|
|
1492
|
-
// take.
|
|
1493
|
-
//
|
|
1494
|
-
// The BASELINE is an argument, not a snapshot taken here: the loop
|
|
1495
|
-
// records it BEFORE each vet run, so a save landing between the run's
|
|
1496
|
-
// reads and the wait still compares as a change. A waiter that
|
|
1497
|
-
// snapshotted on entry would adopt that unvetted save as its baseline
|
|
1498
|
-
// and wait indefinitely on a stale report.
|
|
1499
1329
|
async function watchChange(
|
|
1500
1330
|
files: string[], before: string, pollMs: number): Promise<boolean> {
|
|
1501
1331
|
for (;;) {
|
|
@@ -1535,9 +1365,6 @@ async function watchVet(
|
|
|
1535
1365
|
}
|
|
1536
1366
|
|
|
1537
1367
|
|
|
1538
|
-
// The vet verb. Non-watch runs are synchronous and return the exit
|
|
1539
|
-
// class directly; `--watch` returns a promise that resolves only when
|
|
1540
|
-
// the waiter says stop (never, for the real one).
|
|
1541
1368
|
function runVet(argv: string[], wait?: VetWaiter): number | Promise<number> {
|
|
1542
1369
|
const trusted = takeTrust(argv)
|
|
1543
1370
|
if (null == trusted) {
|
|
@@ -1715,14 +1542,6 @@ type BreakingArgs = {
|
|
|
1715
1542
|
file: string
|
|
1716
1543
|
against: string[]
|
|
1717
1544
|
mode?: BreakingMode
|
|
1718
|
-
// `--at`: compare a SUBTREE of both versions. The gate's own
|
|
1719
|
-
// sub-question, and the one a real repository needs -- a document's
|
|
1720
|
-
// top level carries the module's version string and its policy
|
|
1721
|
-
// block, which are supposed to change between releases and which
|
|
1722
|
-
// make the whole-document comparison answer about them rather than
|
|
1723
|
-
// about the contract (use-cases/REVIEW.md finding D). `subsume` has
|
|
1724
|
-
// taken it since G3; `breaking` did not, so the only way to gate a
|
|
1725
|
-
// subtree was to split the file.
|
|
1726
1545
|
at?: string
|
|
1727
1546
|
allowUndecided: boolean
|
|
1728
1547
|
allowDeprecatedRemoval: boolean
|
|
@@ -1806,36 +1625,10 @@ function parseBreakingArgs(
|
|
|
1806
1625
|
}
|
|
1807
1626
|
}
|
|
1808
1627
|
|
|
1809
|
-
// One resolved `--against` spelling: the old document's text, the path
|
|
1810
|
-
// its own relative includes must resolve from, and (for a git spelling)
|
|
1811
|
-
// the temporary tree to remove when the run is done.
|
|
1812
1628
|
type OldVersion = { src: string, path: string, temp?: string }
|
|
1813
1629
|
|
|
1814
|
-
// A source file the include resolver can actually load. `git#<rev>`
|
|
1815
|
-
// materialises these and nothing else: an include names an Aontu
|
|
1816
|
-
// document (`.aon`/`.aontu`, the two extensions `@"foo"` tries) or a
|
|
1817
|
-
// JSON one, so the rest of a revision's tree cannot be part of any
|
|
1818
|
-
// include closure and copying it would be pure cost.
|
|
1819
1630
|
const INCLUDABLE = /\.(aon|aontu|jsonic|json)$/
|
|
1820
1631
|
|
|
1821
|
-
// Resolve one --against spelling to an old version.
|
|
1822
|
-
//
|
|
1823
|
-
// A `git#<rev>` spelling is the old version of the WHOLE TREE, not of
|
|
1824
|
-
// the entry file alone. It used to be `git show <rev>:./<file>`, whose
|
|
1825
|
-
// text was then evaluated with `generalPath`/`specificPath` pointing at
|
|
1826
|
-
// the WORKING file -- so every `@"..."` include in the old document
|
|
1827
|
-
// resolved against the working tree, and the "old" side was old entry
|
|
1828
|
-
// text meeting new includes. A breaking change inside an included file
|
|
1829
|
-
// therefore compared against itself and answered `compatible`: the
|
|
1830
|
-
// documented CI gate silently un-gated every non-entry file of the
|
|
1831
|
-
// multi-file layout real models use (use-cases/BUGS.md §26). The old
|
|
1832
|
-
// tree's includable sources are copied into a temporary directory and
|
|
1833
|
-
// the old document is evaluated from THERE.
|
|
1834
|
-
//
|
|
1835
|
-
// Sources outside the revision -- package includes under node_modules,
|
|
1836
|
-
// the bundled `std/system` -- still resolve as they do today: they are
|
|
1837
|
-
// not in the tree, and their versions travel with the lockfile rather
|
|
1838
|
-
// than with this comparison.
|
|
1839
1632
|
function oldVersion(spec: string, file: string): OldVersion | undefined {
|
|
1840
1633
|
if (!spec.startsWith('git#')) {
|
|
1841
1634
|
try {
|
|
@@ -1867,24 +1660,10 @@ function oldVersion(spec: string, file: string): OldVersion | undefined {
|
|
|
1867
1660
|
// that only some failures take.
|
|
1868
1661
|
const temp = mkdtempSync(join(tmpdir(), 'aontu-against-'))
|
|
1869
1662
|
try {
|
|
1870
|
-
// THE REPO-RELATIVE PATH COMES FROM GIT, not from path arithmetic.
|
|
1871
|
-
// Relativising `rev-parse --show-toplevel` against `resolve(file)`
|
|
1872
|
-
// puts two DIFFERENT COORDINATE SYSTEMS on either side of the
|
|
1873
|
-
// subtraction: git prints the real path, while the caller's is
|
|
1874
|
-
// whatever they typed. On macOS a temp file under /var is
|
|
1875
|
-
// /private/var to git, and on Windows a TMP short name
|
|
1876
|
-
// (RUNNER~1) is the long form to git -- so the subtraction gave a
|
|
1877
|
-
// `../..` climb, the entry was "not in that revision", and the
|
|
1878
|
-
// documented CI spelling failed on both platforms while passing on
|
|
1879
|
-
// Linux (this PR's own CI). `--show-prefix` is the same question
|
|
1880
|
-
// asked in git's coordinates: the repo-relative directory of the
|
|
1881
|
-
// cwd, already slash-separated and already normalised.
|
|
1882
1663
|
const prefix = git(['rev-parse', '--show-prefix'], dir).trim()
|
|
1883
1664
|
const entryRel = prefix + basename(file)
|
|
1884
1665
|
const top = git(['rev-parse', '--show-toplevel'], dir).trim()
|
|
1885
1666
|
|
|
1886
|
-
// `-z` so a path with a newline or a quote cannot be mistaken for
|
|
1887
|
-
// two paths (git otherwise quotes such names).
|
|
1888
1667
|
const listed = git(['ls-tree', '-r', '-z', '--name-only', rev], top)
|
|
1889
1668
|
.split('\0').filter((p) => '' !== p)
|
|
1890
1669
|
if (!listed.includes(entryRel)) {
|
|
@@ -1919,13 +1698,6 @@ function policyCompat(
|
|
|
1919
1698
|
): BreakingMode | undefined {
|
|
1920
1699
|
const aontu = new Aontu()
|
|
1921
1700
|
const ctx = aontu.ctx({ collect: true })
|
|
1922
|
-
// The declaration is read by EVALUATING the document, so this leg
|
|
1923
|
-
// runs the include resolver too and has to run it under BOTH of the
|
|
1924
|
-
// verb's include options -- a `breaking --trust none` that read its
|
|
1925
|
-
// own mode through an unconfined resolver would confine the
|
|
1926
|
-
// comparison and not the question (use-cases/REVIEW.md finding G),
|
|
1927
|
-
// and one that took the capability alone read no mode at all when
|
|
1928
|
-
// the declaration arrived through a `--text-ext` include.
|
|
1929
1701
|
const v: any = aontu.unify(newSrc, { path, ...includeOpts(include) }, ctx)
|
|
1930
1702
|
if (0 < ctx.err.length || true === v?.isNil) {
|
|
1931
1703
|
return undefined
|
|
@@ -1945,11 +1717,6 @@ function policyCompat(
|
|
|
1945
1717
|
? m : undefined
|
|
1946
1718
|
}
|
|
1947
1719
|
|
|
1948
|
-
// Is the evaluated old version's value at the finding path deprecated?
|
|
1949
|
-
// The --allow-deprecated-removal downgrade (G3 phase 4): removing (or
|
|
1950
|
-
// otherwise changing) a value the old version already deprecated warns
|
|
1951
|
-
// instead of breaking. The Go port exports the same reader as
|
|
1952
|
-
// aontu.DeprecatedAt.
|
|
1953
1720
|
function deprecatedAt(oldSrc: string, path: string, filePath: string): boolean {
|
|
1954
1721
|
const aontu = new Aontu()
|
|
1955
1722
|
const ctx = aontu.ctx({ collect: true })
|
|
@@ -2024,9 +1791,6 @@ function runBreaking(argv: string[]): number {
|
|
|
2024
1791
|
return 2
|
|
2025
1792
|
}
|
|
2026
1793
|
|
|
2027
|
-
// The declared mode: --mode overrides the document's own policy;
|
|
2028
|
-
// neither means backward, the index's framing (v1-valid documents
|
|
2029
|
-
// stay valid).
|
|
2030
1794
|
const mode: BreakingMode =
|
|
2031
1795
|
args.mode ??
|
|
2032
1796
|
policyCompat(newSrc, args.file,
|
|
@@ -2066,8 +1830,6 @@ function runBreaking(argv: string[]): number {
|
|
|
2066
1830
|
temps.push(old.temp)
|
|
2067
1831
|
}
|
|
2068
1832
|
|
|
2069
|
-
// backward: the NEW document is the general side — every old
|
|
2070
|
-
// instance must still be admitted. forward: the old one is.
|
|
2071
1833
|
const checks: Array<{ general: [string, string], specific: [string, string] }> = []
|
|
2072
1834
|
if ('backward' === mode || 'full' === mode) {
|
|
2073
1835
|
checks.push({ general: [newSrc, args.file], specific: [oldSrc, spec] })
|
|
@@ -2084,18 +1846,10 @@ function runBreaking(argv: string[]): number {
|
|
|
2084
1846
|
at: args.at,
|
|
2085
1847
|
generalUrl: check.general[1],
|
|
2086
1848
|
specificUrl: check.specific[1],
|
|
2087
|
-
// The old side's relative loads resolve from ITS own tree --
|
|
2088
|
-
// the materialised revision for a git spelling, the named
|
|
2089
|
-
// file's directory otherwise -- so an included file's change
|
|
2090
|
-
// is part of the comparison rather than invisible to it.
|
|
2091
1849
|
generalPath: check.general[1] === spec ? oldPath : args.file,
|
|
2092
1850
|
specificPath: check.specific[1] === spec ? oldPath : args.file,
|
|
2093
1851
|
})
|
|
2094
1852
|
|
|
2095
|
-
// The deprecated-removal downgrade: a finding about a value the
|
|
2096
|
-
// OLD version already deprecated becomes a warning, and warnings
|
|
2097
|
-
// do not move the verdict. Deprecate-then-remove is the
|
|
2098
|
-
// supported rename path (the design's own sequencing).
|
|
2099
1853
|
let verdict = report.verdict
|
|
2100
1854
|
if (args.allowDeprecatedRemoval) {
|
|
2101
1855
|
let liveFindings = 0
|
|
@@ -2153,13 +1907,6 @@ function renderBreakingJson(report: SubsumeReport, mode: string): string {
|
|
|
2153
1907
|
}
|
|
2154
1908
|
|
|
2155
1909
|
|
|
2156
|
-
// ---------------------------------------------------------------------
|
|
2157
|
-
// The trim reporter (G3 phase 6): report redundant entries as paths.
|
|
2158
|
-
// Report-only — REWRITING needs G7's format-preserving patch surface —
|
|
2159
|
-
// which is why --check is REQUIRED rather than defaulted: `aontu trim
|
|
2160
|
-
// f.aon` reads as "trim this file", and doing something else silently
|
|
2161
|
-
// is worse than saying so.
|
|
2162
|
-
|
|
2163
1910
|
const TRIM_HELP = 'aontu trim --check <file> (try --help)'
|
|
2164
1911
|
|
|
2165
1912
|
const TRIM_EXIT: Record<TrimVerdict, number> = {
|
|
@@ -2237,8 +1984,6 @@ function runTrim(argv: string[]): number {
|
|
|
2237
1984
|
|
|
2238
1985
|
function renderTrimText(report: TrimReport): string {
|
|
2239
1986
|
const head = `verdict: ${report.verdict}`
|
|
2240
|
-
// WHY, when the document could not be evaluated at all: rendered as
|
|
2241
|
-
// vet renders a finding, because it IS one (the review's finding F).
|
|
2242
1987
|
const errors = report.errors ?? []
|
|
2243
1988
|
if (0 < errors.length) {
|
|
2244
1989
|
return [head, ''].concat(errors.map(renderFinding)).join('\n')
|
|
@@ -2303,29 +2048,12 @@ const VIEW_EDGES: ViewEdges[] = ['upward', 'all', 'none']
|
|
|
2303
2048
|
// the same division err.ts already draws for the error frames.
|
|
2304
2049
|
const VIEW_STYLES = ['auto', 'none', 'ansi', 'css']
|
|
2305
2050
|
|
|
2306
|
-
// `--style auto` resolved, which only the CLI can do. The mechanism is
|
|
2307
|
-
// the PROFILE's and the library knows it -- an SVG carries its
|
|
2308
|
-
// stylesheet unless told not to, which is what makes a figure stand
|
|
2309
|
-
// alone. What the library cannot know is whether the DESTINATION is a
|
|
2310
|
-
// terminal, so that is the only thing decided here: escapes on the
|
|
2311
|
-
// text profile when stdout is a terminal and NO_COLOR is unset, the
|
|
2312
|
-
// same two conditions the error frames use. `undefined` leaves the
|
|
2313
|
-
// profile's own default in place.
|
|
2314
2051
|
function viewStyleOf(
|
|
2315
2052
|
asked: string | undefined, as: ViewProfile | undefined
|
|
2316
2053
|
): ViewStyle | undefined {
|
|
2317
2054
|
if (undefined !== asked && 'auto' !== asked) {
|
|
2318
2055
|
return asked as ViewStyle
|
|
2319
2056
|
}
|
|
2320
|
-
// STDOUT'S OWN TERMINAL-NESS, and NO_COLOR read here rather than
|
|
2321
|
-
// through colorActive(). The figure goes to STDOUT and the error
|
|
2322
|
-
// frames go to STDERR, and they are not the same destination: main()
|
|
2323
|
-
// has already called setColor for stderr, so asking colorActive()
|
|
2324
|
-
// would answer the wrong question twice --- no escapes for
|
|
2325
|
-
// `aontu view tree m.aon 2>/dev/null` at a terminal, and escapes
|
|
2326
|
-
// into the pipe for `aontu view tree m.aon | less`. The NO_COLOR
|
|
2327
|
-
// rule is the one no-color.org states and err.ts implements:
|
|
2328
|
-
// set, to anything but empty, means no colour.
|
|
2329
2057
|
const no = process.env.NO_COLOR
|
|
2330
2058
|
return 'text' === as && true === process.stdout.isTTY
|
|
2331
2059
|
&& (null == no || '' === no) ? 'ansi' : undefined
|
|
@@ -2351,24 +2079,6 @@ const VIEW_USAGE_CODES = [
|
|
|
2351
2079
|
|
|
2352
2080
|
const MOD_HELP = 'aontu mod tidy|verify|vendor|manifest [dir] (try --help)'
|
|
2353
2081
|
|
|
2354
|
-
// The module tooling (G6 phase 3, ts/src/mod-tool.ts). All LOCAL:
|
|
2355
|
-
// `tidy` resolves the closure from what is in the stores and rewrites
|
|
2356
|
-
// the lockfile, `verify` asks whether the stores still mean what the
|
|
2357
|
-
// lockfile pins and changes nothing, `vendor` materialises the locked
|
|
2358
|
-
// closure into the project, `manifest` prints what a publish would
|
|
2359
|
-
// push.
|
|
2360
|
-
//
|
|
2361
|
-
// TIDY AND VERIFY ARE DIFFERENT QUESTIONS, and that is why both exist.
|
|
2362
|
-
// Tidy recomputes and rewrites by design -- a pin is what a module
|
|
2363
|
-
// means NOW -- so it makes the lockfile agree with whatever the store
|
|
2364
|
-
// holds, tampering included. Verify is the gate: a CI job runs it
|
|
2365
|
-
// BEFORE tidy, or instead of it.
|
|
2366
|
-
//
|
|
2367
|
-
// `get` and `publish` are the NETWORK half of the design and are not in
|
|
2368
|
-
// this build. They are named here rather than left to fall out as an
|
|
2369
|
-
// unknown subcommand, because a reader of the design will type them and
|
|
2370
|
-
// deserves to be told which half is missing rather than that the word
|
|
2371
|
-
// is wrong.
|
|
2372
2082
|
function runMod(argv: string[]): number {
|
|
2373
2083
|
const rest: string[] = []
|
|
2374
2084
|
let format: SubsumeFormat = 'text'
|
|
@@ -2422,10 +2132,6 @@ function runMod(argv: string[]): number {
|
|
|
2422
2132
|
return 2
|
|
2423
2133
|
}
|
|
2424
2134
|
|
|
2425
|
-
// THE OLD LAYOUT IS NAMED, NOT READ. The lockfile and the vendored
|
|
2426
|
-
// closure moved under aontu_meta/; a project that still carries them
|
|
2427
|
-
// at its root would otherwise look untouched by any of these verbs,
|
|
2428
|
-
// which is the one silence worth breaking.
|
|
2429
2135
|
if (existsSync(join(dir, 'aon_vendor')) || existsSync(join(dir, 'mod-lock.aon'))) {
|
|
2430
2136
|
process.stderr.write(
|
|
2431
2137
|
'aontu: aon_vendor/ and mod-lock.aon now live under aontu_meta/: ' +
|
|
@@ -2469,9 +2175,6 @@ type ModVerdict =
|
|
|
2469
2175
|
const MOD_EXIT: Record<ModVerdict, number> = {
|
|
2470
2176
|
ok: 0,
|
|
2471
2177
|
missing: 1,
|
|
2472
|
-
// A REFUSED GATE, with `breaking`: a store that no longer means what
|
|
2473
|
-
// the lockfile pins is the integrity check saying no, and a CI job
|
|
2474
|
-
// reading exit codes should not have to learn a third class for it.
|
|
2475
2178
|
mismatch: 1,
|
|
2476
2179
|
// Likewise a lockfile that does not cover the project: the gate has
|
|
2477
2180
|
// nothing to check, which is a refusal and not a pass.
|
|
@@ -2522,11 +2225,6 @@ function modText(sub: string, report: any): string {
|
|
|
2522
2225
|
for (const f of report.findings) {
|
|
2523
2226
|
lines.push(f.path + ': ' + f.message)
|
|
2524
2227
|
}
|
|
2525
|
-
// What a manifest lacks is a declaration the module does not make
|
|
2526
|
-
// or an entry file that is not there, and neither is something a
|
|
2527
|
-
// fetch would supply -- so this is not the tail the other two
|
|
2528
|
-
// subcommands share. The name says which kind it is: `mod.version`
|
|
2529
|
-
// is a declaration, `service.aon` is a file.
|
|
2530
2228
|
for (const miss of report.missing) {
|
|
2531
2229
|
lines.push(miss + ': missing')
|
|
2532
2230
|
}
|
|
@@ -2537,8 +2235,6 @@ function modText(sub: string, report: any): string {
|
|
|
2537
2235
|
for (const mod of report.verified) {
|
|
2538
2236
|
lines.push(mod + ': verified')
|
|
2539
2237
|
}
|
|
2540
|
-
// BOTH HASHES, because the useful question is which way it moved:
|
|
2541
|
-
// an empty `got` is a module that no longer stands up at all.
|
|
2542
2238
|
for (const m of report.mismatched) {
|
|
2543
2239
|
lines.push(m.mod + ': pinned ' + m.want + ' but the store means ' +
|
|
2544
2240
|
('' === m.got ? 'nothing (it does not evaluate)' : m.got))
|
|
@@ -2574,26 +2270,6 @@ function modText(sub: string, report: any): string {
|
|
|
2574
2270
|
}
|
|
2575
2271
|
|
|
2576
2272
|
|
|
2577
|
-
// VACUITY SIGNALS (G11 phase 4,
|
|
2578
|
-
// docs/capability-review/g11-agent-onramp.md).
|
|
2579
|
-
//
|
|
2580
|
-
// The same principle phase 5 applied to `vet`: a verb that did NOTHING
|
|
2581
|
-
// and a verb that did its job answer the same. `aontu view tree` over a
|
|
2582
|
-
// document declaring no relations printed one newline and exited 0;
|
|
2583
|
-
// `aontu render` with no profile printed nothing and exited 0; `aontu
|
|
2584
|
-
// relations` over a document declaring none answered `verdict: pass`.
|
|
2585
|
-
// For a person at a terminal that is a shrug. For an unattended agent
|
|
2586
|
-
// it is a green check mark on an empty box.
|
|
2587
|
-
//
|
|
2588
|
-
// ON STDERR, ALWAYS. stdout is a report contract -- a `--format json`
|
|
2589
|
-
// consumer parses it -- and the exit code is a verdict class that
|
|
2590
|
-
// callers already branch on. Neither changes here: what changes is
|
|
2591
|
-
// that the caller is TOLD. A caller who wants it to be fatal has
|
|
2592
|
-
// `vet --strict-coverage`, and the same argument would give the other
|
|
2593
|
-
// verbs a flag of their own if one is ever asked for.
|
|
2594
|
-
//
|
|
2595
|
-
// The repository already ruled this for one verb, in G8 phase 6 on
|
|
2596
|
-
// `trim`: "doing something else silently is worse than refusing".
|
|
2597
2273
|
function vacuous(what: string, why: string): void {
|
|
2598
2274
|
process.stderr.write(`aontu: ${what}: ${why}\n`)
|
|
2599
2275
|
}
|
|
@@ -2881,12 +2557,6 @@ function runView(argv: string[]): number {
|
|
|
2881
2557
|
}
|
|
2882
2558
|
}
|
|
2883
2559
|
|
|
2884
|
-
// ESCAPES NEVER GO INTO A FILE. A pinned golden holding terminal
|
|
2885
|
-
// control codes is not a golden anybody can read, and a byte
|
|
2886
|
-
// comparison against one would fail on the reader's terminal
|
|
2887
|
-
// settings. `auto` resolves to `none` there on its own; asking for
|
|
2888
|
-
// `ansi` explicitly is a usage error rather than a silent downgrade,
|
|
2889
|
-
// so a script that wanted colour is told where it went.
|
|
2890
2560
|
if ('ansi' === style && (undefined !== out || undefined !== opts.views)) {
|
|
2891
2561
|
process.stderr.write(
|
|
2892
2562
|
'aontu: --style ansi writes to a terminal, not to a file\n')
|
|
@@ -2896,10 +2566,6 @@ function runView(argv: string[]): number {
|
|
|
2896
2566
|
// THE VIEW DOCUMENT draws every figure a document declares, so it
|
|
2897
2567
|
// names no kind: the declarations do, one each.
|
|
2898
2568
|
if (undefined !== opts.views) {
|
|
2899
|
-
// A declaration names its own profile, so the style is left to
|
|
2900
|
-
// each figure's own default; `--style none` still reaches every
|
|
2901
|
-
// one of them, which is how a host page that binds the CSS
|
|
2902
|
-
// variables asks for eight figures without eight stylesheets.
|
|
2903
2569
|
opts.style = viewStyleOf(style, undefined)
|
|
2904
2570
|
return runViewSet(rest, opts, trust, { format, check, strict, out })
|
|
2905
2571
|
}
|
|
@@ -2975,17 +2641,6 @@ function runView(argv: string[]): number {
|
|
|
2975
2641
|
}
|
|
2976
2642
|
const report = view(srcs[0], viewOpts)
|
|
2977
2643
|
|
|
2978
|
-
// AN EMPTY FIGURE IS THE SAME BYTES AS A DRAWN ONE MINUS ITS
|
|
2979
|
-
// CONTENT, and every profile spells "empty" differently: text draws
|
|
2980
|
-
// nothing at all, mermaid still draws its `flowchart LR` header, the
|
|
2981
|
-
// matrix still prints its count line. Rather than teach this one
|
|
2982
|
-
// place each of those spellings -- a list that goes stale the first
|
|
2983
|
-
// time a profile gains a header -- ASK THE SAME KIND TO DRAW AN
|
|
2984
|
-
// EMPTY DOCUMENT and compare. Equal texts mean this document
|
|
2985
|
-
// contributed nothing to the figure, whatever the profile.
|
|
2986
|
-
//
|
|
2987
|
-
// It costs one drawing of `{}`, which is the cheapest document
|
|
2988
|
-
// there is, and only on a run that produced a figure at all.
|
|
2989
2644
|
if ('error' !== report.verdict && null != report.text) {
|
|
2990
2645
|
const bare = view('{}', viewOpts)
|
|
2991
2646
|
if ('error' !== bare.verdict && bare.text === report.text) {
|
|
@@ -3039,13 +2694,6 @@ function runView(argv: string[]): number {
|
|
|
3039
2694
|
return strict && 'lossy' === report.verdict ? 1 : VIEW_EXIT[report.verdict]
|
|
3040
2695
|
}
|
|
3041
2696
|
|
|
3042
|
-
// `aontu view --views <path> <file>`: every figure the document
|
|
3043
|
-
// declares, from one evaluation, all or nothing.
|
|
3044
|
-
//
|
|
3045
|
-
// A declared `out` is resolved against the DOCUMENT's own directory,
|
|
3046
|
-
// not the caller's: a view document is committed beside the figures it
|
|
3047
|
-
// gates, and a gate that only passes from one working directory is not
|
|
3048
|
-
// a gate.
|
|
3049
2697
|
function runViewSet(
|
|
3050
2698
|
rest: string[], opts: ViewOptions, trust: TrustArg,
|
|
3051
2699
|
how: { format: SubsumeFormat, check: boolean, strict: boolean, out?: string }
|
|
@@ -3186,8 +2834,6 @@ function renderViewJson(report: ViewReport): string {
|
|
|
3186
2834
|
|
|
3187
2835
|
function renderRelationsText(report: RelationReport): string {
|
|
3188
2836
|
const head = `verdict: ${report.verdict}`
|
|
3189
|
-
// WHY, when the document could not be evaluated at all: rendered as
|
|
3190
|
-
// vet renders a finding, because it IS one (the review's finding F).
|
|
3191
2837
|
const errors = report.errors ?? []
|
|
3192
2838
|
if (0 < errors.length) {
|
|
3193
2839
|
return [head, ''].concat(errors.map(renderFinding)).join('\n')
|
|
@@ -3213,21 +2859,6 @@ function renderRelationsJson(report: RelationReport): string {
|
|
|
3213
2859
|
}
|
|
3214
2860
|
|
|
3215
2861
|
|
|
3216
|
-
|
|
3217
|
-
// ---------------------------------------------------------------------
|
|
3218
|
-
// JSON SCHEMA EXPORT (SUPPORT.md act 2, the review's finding I): the
|
|
3219
|
-
// bridge to every structured-output API, which constrains generation to
|
|
3220
|
-
// JSON Schema and nothing else. Export the model, let the provider
|
|
3221
|
-
// generate under it, then `vet` the result against the model itself --
|
|
3222
|
-
// the hybrid an enterprise actually deploys, and impossible without
|
|
3223
|
-
// this verb.
|
|
3224
|
-
//
|
|
3225
|
-
// THE SCHEMA GOES TO STDOUT AND THE LOSSES TO STDERR, so `aontu
|
|
3226
|
-
// jsonschema x.aon > schema.json` writes a schema and still tells the
|
|
3227
|
-
// reader what it could not carry. `--strict` makes a loss a refusal,
|
|
3228
|
-
// for the CI job that would rather fail than ship a schema weaker than
|
|
3229
|
-
// its model.
|
|
3230
|
-
|
|
3231
2862
|
const JSONSCHEMA_HELP =
|
|
3232
2863
|
'aontu jsonschema [--at <path>] [--strict] <file> (try --help)'
|
|
3233
2864
|
|
|
@@ -3323,14 +2954,6 @@ function runJsonSchema(argv: string[]): number {
|
|
|
3323
2954
|
strict && 'lossy' === report.verdict ? 1 : 0
|
|
3324
2955
|
}
|
|
3325
2956
|
|
|
3326
|
-
// ---------------------------------------------------------------------
|
|
3327
|
-
// THE RENDER VERB (docs/design/RENDER.0.md D8): evaluate a document,
|
|
3328
|
-
// vet the value at --at against aontu:code, fold code.units into bytes,
|
|
3329
|
-
// and put them where the flag says -- one unit on stdout, every unit
|
|
3330
|
-
// below --out (all or nothing), or compared against --check. Exit codes
|
|
3331
|
-
// mirror jsonschema's: 0 ok; 1 lossy under --strict or drift under
|
|
3332
|
-
// --check; 2 usage or I/O, a refused unit path included; 4 the
|
|
3333
|
-
// document does not stand up or the instance is not aontu:code.
|
|
3334
2957
|
|
|
3335
2958
|
const RENDER_HELP =
|
|
3336
2959
|
'aontu render [--at <path>] [--profile <file>]... [--unit <path>] ' +
|
|
@@ -3468,47 +3091,15 @@ function runRender(argv: string[]): number {
|
|
|
3468
3091
|
return 2
|
|
3469
3092
|
}
|
|
3470
3093
|
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
// carries the target's extension and never `.aon`. Desugared here
|
|
3475
|
-
// rather than anywhere deeper, because a template is an entry
|
|
3476
|
-
// spelling and not a value: an include is still aontu.
|
|
3477
|
-
if (!files[0].endsWith('.aon')) {
|
|
3478
|
-
src = desugarTemplate(src, marker ?? markerFor(files[0]))
|
|
3094
|
+
const loadedProfiles = loadProfiles(profileFiles, trust)
|
|
3095
|
+
if ('number' === typeof loadedProfiles) {
|
|
3096
|
+
return loadedProfiles
|
|
3479
3097
|
}
|
|
3098
|
+
const profiles = loadedProfiles
|
|
3480
3099
|
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
// (renderProfile, which also fills the defaults). Two files claiming
|
|
3485
|
-
// one lang is a usage error: the fold could not choose.
|
|
3486
|
-
const profiles: any[] = []
|
|
3487
|
-
const langs = new Map<string, string>()
|
|
3488
|
-
for (const pf of profileFiles) {
|
|
3489
|
-
let text: string
|
|
3490
|
-
try {
|
|
3491
|
-
text = readFileSync(pf, 'utf8')
|
|
3492
|
-
}
|
|
3493
|
-
catch (err: any) {
|
|
3494
|
-
process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`)
|
|
3495
|
-
return 2
|
|
3496
|
-
}
|
|
3497
|
-
const loaded = renderProfile(text,
|
|
3498
|
-
{ path: resolve(pf), ...verbOpts(trust, entryRootOf(pf)) })
|
|
3499
|
-
if (undefined !== loaded.errors) {
|
|
3500
|
-
process.stderr.write(loaded.errors.map(renderFinding).join('\n') + '\n')
|
|
3501
|
-
return 4
|
|
3502
|
-
}
|
|
3503
|
-
const profile = loaded.profile
|
|
3504
|
-
const prev = langs.get(profile.lang)
|
|
3505
|
-
if (undefined !== prev) {
|
|
3506
|
-
process.stderr.write(
|
|
3507
|
-
`aontu: two profiles claim ${profile.lang}: ${prev} and ${pf}\n`)
|
|
3508
|
-
return 2
|
|
3509
|
-
}
|
|
3510
|
-
langs.set(profile.lang, pf)
|
|
3511
|
-
profiles.push(profile)
|
|
3100
|
+
if (!files[0].endsWith('.aon')) {
|
|
3101
|
+
src = desugarTemplate(src, marker ??
|
|
3102
|
+
markerFromProfiles(profiles, files[0]) ?? markerFor(files[0]))
|
|
3512
3103
|
}
|
|
3513
3104
|
|
|
3514
3105
|
// A RENDER WITH NO PROFILE PRODUCES NO UNITS, and said so with zero
|
|
@@ -3660,21 +3251,21 @@ function renderExit(report: RenderReport, drift: number): number {
|
|
|
3660
3251
|
}
|
|
3661
3252
|
|
|
3662
3253
|
|
|
3663
|
-
// ---------------------------------------------------------------------
|
|
3664
|
-
// THE TEMPLATE SURFACE (docs/design/TEMPLATE.0.md; RENDER.0.md P8): the
|
|
3665
|
-
// two transforms and the round trip between them. `render` reads a
|
|
3666
|
-
// template directly, by its extension; this verb is for seeing the
|
|
3667
|
-
// canonical form, for writing one by hand and sugaring it, and for the
|
|
3668
|
-
// check that keeps a committed template and its meaning in agreement.
|
|
3669
|
-
|
|
3670
3254
|
const TEMPLATE_HELP =
|
|
3671
3255
|
'aontu template [--resugar] [--check] [--marker <token>] <file> (try --help)'
|
|
3672
3256
|
|
|
3673
3257
|
function runTemplate(argv: string[]): number {
|
|
3258
|
+
const trusted = takeTrust(argv)
|
|
3259
|
+
if (null == trusted) {
|
|
3260
|
+
return 2
|
|
3261
|
+
}
|
|
3262
|
+
argv = trusted.argv
|
|
3263
|
+
const trust = trusted.trust
|
|
3674
3264
|
const files: string[] = []
|
|
3675
3265
|
let resugar = false
|
|
3676
3266
|
let check = false
|
|
3677
3267
|
let marker: string | undefined = undefined
|
|
3268
|
+
const profileFiles: string[] = []
|
|
3678
3269
|
|
|
3679
3270
|
for (let i = 0; i < argv.length; i++) {
|
|
3680
3271
|
const arg = argv[i]
|
|
@@ -3695,6 +3286,14 @@ function runTemplate(argv: string[]): number {
|
|
|
3695
3286
|
return 2
|
|
3696
3287
|
}
|
|
3697
3288
|
}
|
|
3289
|
+
else if ('--profile' === arg) {
|
|
3290
|
+
const pf = argv[++i]
|
|
3291
|
+
if (null == pf) {
|
|
3292
|
+
process.stderr.write('aontu: --profile needs a file\n')
|
|
3293
|
+
return 2
|
|
3294
|
+
}
|
|
3295
|
+
profileFiles.push(pf)
|
|
3296
|
+
}
|
|
3698
3297
|
else if (arg.startsWith('-')) {
|
|
3699
3298
|
process.stderr.write(
|
|
3700
3299
|
`aontu: unknown template option ${arg} (try --help)\n`)
|
|
@@ -3709,10 +3308,6 @@ function runTemplate(argv: string[]): number {
|
|
|
3709
3308
|
process.stderr.write(`aontu: template needs one file\n${TEMPLATE_HELP}\n`)
|
|
3710
3309
|
return 2
|
|
3711
3310
|
}
|
|
3712
|
-
// THE TWO ARE DIRECTIONS, NOT MODES THAT COMPOSE: `--check` reads a
|
|
3713
|
-
// template and asks whether the round trip answers it back, and
|
|
3714
|
-
// `--resugar` reads the canonical form instead. A run cannot be both
|
|
3715
|
-
// at once, because the file is one thing or the other.
|
|
3716
3311
|
if (resugar && check) {
|
|
3717
3312
|
process.stderr.write(
|
|
3718
3313
|
'aontu: template takes one of --resugar or --check\n')
|
|
@@ -3728,19 +3323,15 @@ function runTemplate(argv: string[]): number {
|
|
|
3728
3323
|
return 2
|
|
3729
3324
|
}
|
|
3730
3325
|
|
|
3731
|
-
const
|
|
3326
|
+
const declared = loadProfiles(profileFiles, trust)
|
|
3327
|
+
if ('number' === typeof declared) {
|
|
3328
|
+
return declared
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
const mark = marker ?? markerFromProfiles(declared, files[0]) ??
|
|
3332
|
+
markerFor(files[0])
|
|
3732
3333
|
|
|
3733
3334
|
if (check) {
|
|
3734
|
-
// THE ROUND TRIP IS THE CHECK (D6): the file held to the spelling
|
|
3735
|
-
// the two transforms answer. What that names is a marker line the
|
|
3736
|
-
// transform would not have written -- one without its space, or one
|
|
3737
|
-
// whose aontu is indented after the marker rather than before it,
|
|
3738
|
-
// since the marker keeps its own indentation. It does NOT name a
|
|
3739
|
-
// changed body line: a template's whitespace is output, so a
|
|
3740
|
-
// trimmed trailing space is still a valid template and it is
|
|
3741
|
-
// `render --check` against the committed files that catches it.
|
|
3742
|
-
// The first line that differs is the report, since a whole diff of
|
|
3743
|
-
// a generator is the file again.
|
|
3744
3335
|
const back = resugarTemplate(desugarTemplate(src, mark), mark)
|
|
3745
3336
|
if (back === src) {
|
|
3746
3337
|
return 0
|
|
@@ -3751,12 +3342,6 @@ function runTemplate(argv: string[]): number {
|
|
|
3751
3342
|
while (n < want.length && n < have.length && want[n] === have[n]) {
|
|
3752
3343
|
n++
|
|
3753
3344
|
}
|
|
3754
|
-
// THE TWO ARE THE SAME LENGTH, always: each transform maps one
|
|
3755
|
-
// line to one line and applies the same trailing-newline rule, so
|
|
3756
|
-
// `back` has as many lines as `src`. The loop above therefore stops
|
|
3757
|
-
// at a real difference rather than by running out of either -- an
|
|
3758
|
-
// equal prefix all the way to the end IS `back === src`, which
|
|
3759
|
-
// returned above. So both indexes are in range here.
|
|
3760
3345
|
process.stderr.write(
|
|
3761
3346
|
`aontu: ${files[0]}:${n + 1} is not what the round trip answers\n` +
|
|
3762
3347
|
` have: ${JSON.stringify(have[n])}\n` +
|
|
@@ -3770,13 +3355,43 @@ function runTemplate(argv: string[]): number {
|
|
|
3770
3355
|
}
|
|
3771
3356
|
|
|
3772
3357
|
|
|
3773
|
-
//
|
|
3774
|
-
//
|
|
3775
|
-
//
|
|
3776
|
-
//
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3358
|
+
// The profiles named by --profile, vetted, or the exit code that says
|
|
3359
|
+
// why not. A profile is a language declared as data: `render` matches
|
|
3360
|
+
// one to a unit by `lang`, and `template` and `fmt` match one to a file
|
|
3361
|
+
// by the extensions its `template.ext` names.
|
|
3362
|
+
function loadProfiles(
|
|
3363
|
+
profileFiles: string[], trust: TrustArg
|
|
3364
|
+
): any[] | number {
|
|
3365
|
+
const profiles: any[] = []
|
|
3366
|
+
const langs = new Map<string, string>()
|
|
3367
|
+
for (const pf of profileFiles) {
|
|
3368
|
+
let text: string
|
|
3369
|
+
try {
|
|
3370
|
+
text = readFileSync(pf, 'utf8')
|
|
3371
|
+
}
|
|
3372
|
+
catch (err: any) {
|
|
3373
|
+
process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`)
|
|
3374
|
+
return 2
|
|
3375
|
+
}
|
|
3376
|
+
const loaded = renderProfile(text,
|
|
3377
|
+
{ path: resolve(pf), ...verbOpts(trust, entryRootOf(pf)) })
|
|
3378
|
+
if (undefined !== loaded.errors) {
|
|
3379
|
+
process.stderr.write(loaded.errors.map(renderFinding).join('\n') + '\n')
|
|
3380
|
+
return 4
|
|
3381
|
+
}
|
|
3382
|
+
const profile = loaded.profile
|
|
3383
|
+
const prev = langs.get(profile.lang)
|
|
3384
|
+
if (undefined !== prev) {
|
|
3385
|
+
process.stderr.write(
|
|
3386
|
+
`aontu: two profiles claim ${profile.lang}: ${prev} and ${pf}\n`)
|
|
3387
|
+
return 2
|
|
3388
|
+
}
|
|
3389
|
+
langs.set(profile.lang, pf)
|
|
3390
|
+
profiles.push(profile)
|
|
3391
|
+
}
|
|
3392
|
+
return profiles
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3780
3395
|
|
|
3781
3396
|
const HASH_HELP = 'aontu hash <file> (try --help)'
|
|
3782
3397
|
|
|
@@ -3837,14 +3452,6 @@ function runHash(argv: string[]): number {
|
|
|
3837
3452
|
const ctx = aontu.ctx({ collect: true })
|
|
3838
3453
|
const v: any = aontu.unify(src, { path: files[0] }, ctx)
|
|
3839
3454
|
if (0 < ctx.err.length || true === v?.isNil) {
|
|
3840
|
-
// A document that does not stand up on its own has no meaning to
|
|
3841
|
-
// pin, and a hash of a broken evaluation would be a pin that
|
|
3842
|
-
// silently agrees with every other broken evaluation.
|
|
3843
|
-
// WHY it does not stand up, not just that it does not: the same
|
|
3844
|
-
// diagnosis `aontu <file>` prints (the review's finding F).
|
|
3845
|
-
// evalFailure unconditionally, as every other call site does: it
|
|
3846
|
-
// owns the "ctx.err is never empty here" contract, and a guard
|
|
3847
|
-
// that pretends otherwise is a dead arm asserting nothing.
|
|
3848
3455
|
process.stderr.write(
|
|
3849
3456
|
`aontu: ${files[0]} does not evaluate on its own; nothing to hash\n` +
|
|
3850
3457
|
renderFinding(evalFailure(ctx)) + '\n')
|
|
@@ -3863,13 +3470,6 @@ function runHash(argv: string[]): number {
|
|
|
3863
3470
|
}
|
|
3864
3471
|
|
|
3865
3472
|
|
|
3866
|
-
// ---------------------------------------------------------------------
|
|
3867
|
-
// The query surface (G7 phase 1): one node of an evaluated document,
|
|
3868
|
-
// selected by path and rendered. Evaluation is still GLOBAL -- what
|
|
3869
|
-
// `get` buys is the size of the ANSWER, not the cost of producing it --
|
|
3870
|
-
// and the projections are lattice abstractions, each a valid Aontu
|
|
3871
|
-
// document that subsumes the truth it summarises.
|
|
3872
|
-
|
|
3873
3473
|
const GET_HELP = 'aontu get <path> <file> (try --help)'
|
|
3874
3474
|
|
|
3875
3475
|
function runGet(argv: string[]): number {
|
|
@@ -4057,11 +3657,6 @@ function runWhy(argv: string[]): number {
|
|
|
4057
3657
|
}
|
|
4058
3658
|
|
|
4059
3659
|
|
|
4060
|
-
// One contribution per line, numbered in source order, each with what
|
|
4061
|
-
// was written, where, and how it got here. A siteless contribution
|
|
4062
|
-
// prints no location rather than a `-1:-1` that means nothing —
|
|
4063
|
-
// exported for the direct test, because the site SHAPE allows one
|
|
4064
|
-
// while no document has yet produced one (ADR-002).
|
|
4065
3660
|
function renderWhyText(record: WhyRecord): string {
|
|
4066
3661
|
const head = `${record.path} = ${record.value}`
|
|
4067
3662
|
if (0 === record.conjuncts.length) {
|
|
@@ -4079,13 +3674,6 @@ function renderWhyText(record: WhyRecord): string {
|
|
|
4079
3674
|
}
|
|
4080
3675
|
|
|
4081
3676
|
|
|
4082
|
-
// ---------------------------------------------------------------------
|
|
4083
|
-
// The overlay patch verb (G7 phase 5): change a document by APPENDING
|
|
4084
|
-
// to an overlay, not by rewriting it. An overlay entry is just another
|
|
4085
|
-
// conjunct and unification is order-independent, so this needs no
|
|
4086
|
-
// rewriter — the format-preserving in-place edit is stage 2, and needs
|
|
4087
|
-
// a comment-preserving CST the parser stack does not have.
|
|
4088
|
-
|
|
4089
3677
|
const SET_HELP =
|
|
4090
3678
|
'aontu set <path>=<value> --entry <file> --overlay <file> (try --help)'
|
|
4091
3679
|
|
|
@@ -4202,29 +3790,12 @@ function runSet(argv: string[]): number {
|
|
|
4202
3790
|
}, 2) + '\n')
|
|
4203
3791
|
}
|
|
4204
3792
|
else {
|
|
4205
|
-
// A replacement is REPORTED as the edit it is, not left for the
|
|
4206
|
-
// reader to infer from a changed file: `where: what -> what`, in
|
|
4207
|
-
// source spelling, because the spelling is what changed.
|
|
4208
|
-
//
|
|
4209
|
-
// PAST TENSE ONLY WHERE IT HAPPENED. A refused write leaves the
|
|
4210
|
-
// file exactly as it was, and one assignment can be replaceable
|
|
4211
|
-
// while another makes the whole run invalid — so `replaced:` there
|
|
4212
|
-
// tells an operator the pin was changed when it was not, and unlike
|
|
4213
|
-
// `--dry-run` there is nothing else on the line to say otherwise.
|
|
4214
3793
|
const verb = wrote ? 'replaced' : 'would replace'
|
|
4215
3794
|
const edits = report.replaced.map((r) =>
|
|
4216
3795
|
`${verb}: ${r.file}:${r.row}:${r.col} ${r.from} -> ${r.to}`)
|
|
4217
3796
|
const head = [`verdict: ${report.verdict}`].concat(edits).join('\n') +
|
|
4218
3797
|
(wrote ? `\nwrote: ${overlayFile}` : dryRun ? '\n(dry run)' : '')
|
|
4219
3798
|
|
|
4220
|
-
// A SUCCESSFUL COMMAND WRITES ITS STATUS TO STDOUT, findings or
|
|
4221
|
-
// not. Routing on `findings.length` was right while every finding
|
|
4222
|
-
// this verb could produce was an ERROR; `--in-place` made a WARNING
|
|
4223
|
-
// possible, and a run that held, wrote the file and exited 0 then
|
|
4224
|
-
// sent its whole report to stderr — leaving stdout empty, so
|
|
4225
|
-
// `$(aontu set ...)` captured nothing and only the JSON form
|
|
4226
|
-
// behaved like a success. The verdict decides the stream; warnings
|
|
4227
|
-
// are diagnostics and go to stderr beside it.
|
|
4228
3799
|
const failed = 'invalid' === report.verdict || 'error' === report.verdict
|
|
4229
3800
|
const findingText = report.findings.map(renderFinding)
|
|
4230
3801
|
if (failed) {
|
|
@@ -4246,15 +3817,6 @@ function runSet(argv: string[]): number {
|
|
|
4246
3817
|
}
|
|
4247
3818
|
|
|
4248
3819
|
|
|
4249
|
-
// ---------------------------------------------------------------------
|
|
4250
|
-
// The role gate (docs/design/ALLOW.0.md): may the role the caller is
|
|
4251
|
-
// operating under modify these subtrees? Asked before `set`, by an
|
|
4252
|
-
// agent whose skill names its role, and answered from a role model
|
|
4253
|
-
// that is itself an aontu document. The verdict is the exit code, as
|
|
4254
|
-
// it is for every gate here: 0 is yes, 1 is no, 4 is "the model that
|
|
4255
|
-
// was to decide does not stand up", and an agent branches on nothing
|
|
4256
|
-
// else.
|
|
4257
|
-
|
|
4258
3820
|
const ALLOW_HELP =
|
|
4259
3821
|
'aontu allow --role <role> <roles-file> <path> [more-paths...] (try --help)'
|
|
4260
3822
|
|
|
@@ -4372,14 +3934,6 @@ function runAllow(argv: string[]): number {
|
|
|
4372
3934
|
return 2
|
|
4373
3935
|
}
|
|
4374
3936
|
|
|
4375
|
-
// A path may arrive in `set`'s spelling, `$.a.b=1`, so a skill can
|
|
4376
|
-
// hand the gate the very arguments the write will get. The text up
|
|
4377
|
-
// to the first `=` is the path, and it starts with `$`: an empty
|
|
4378
|
-
// argument, or a second file name, would otherwise read as a path
|
|
4379
|
-
// and be answered. The VALUE is checked to be one value. `set`
|
|
4380
|
-
// appends it as source after the flattened path, so a value carrying
|
|
4381
|
-
// a second pair -- `3 secrets: key: "x"` -- writes a sibling of the
|
|
4382
|
-
// overlay root, a subtree the gate was never asked about.
|
|
4383
3937
|
const paths: string[] = []
|
|
4384
3938
|
for (const arg of asked) {
|
|
4385
3939
|
const eq = arg.indexOf('=')
|
|
@@ -4535,35 +4089,23 @@ function runAgentsMd(argv: string[]): number {
|
|
|
4535
4089
|
}
|
|
4536
4090
|
|
|
4537
4091
|
|
|
4538
|
-
// Exit without truncating output.
|
|
4539
|
-
//
|
|
4540
|
-
// process.exit() terminates immediately, discarding anything still
|
|
4541
|
-
// queued on stdout. A write to a PIPE is asynchronous once it exceeds
|
|
4542
|
-
// the pipe buffer, so `write(big); exit(0)` silently truncated output at
|
|
4543
|
-
// 65536 bytes — while a write to a TTY or a file, being synchronous,
|
|
4544
|
-
// looked fine. Setting exitCode instead lets the process end naturally,
|
|
4545
|
-
// after the queue drains.
|
|
4546
|
-
//
|
|
4547
|
-
// This predates the exact leaves but they make it trivially reachable
|
|
4548
|
-
// (one long biginteger canon exceeds the buffer), and it lands squarely
|
|
4549
|
-
// on the parity-probe discipline in AGENTS.md, which derives expected
|
|
4550
|
-
// spec values by piping BOTH CLIs and comparing. A truncated pipe there
|
|
4551
|
-
// reads as a port divergence.
|
|
4552
|
-
// ---------------------------------------------------------------------
|
|
4553
|
-
// The source formatter (docs/design/FMT.0.md): one agreed form, in the
|
|
4554
|
-
// tradition of gofmt. The verb prints, lists, checks, diffs or rewrites;
|
|
4555
|
-
// the form itself is the library's (ts/src/format.ts), and the two
|
|
4556
|
-
// ports agree on it row by row in test/spec/fmt.tsv.
|
|
4557
|
-
|
|
4558
4092
|
const FMT_HELP =
|
|
4559
|
-
'aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>]
|
|
4093
|
+
'aontu fmt [-w|-l|--check|-d|--lint] [--marker <token>] ' +
|
|
4094
|
+
'[--profile <file>] <file>... (try --help)'
|
|
4560
4095
|
|
|
4561
4096
|
type FmtFlags = {
|
|
4562
4097
|
write: boolean, list: boolean, check: boolean, diff: boolean, lint: boolean, strict: boolean,
|
|
4563
4098
|
}
|
|
4564
4099
|
|
|
4565
4100
|
function runFmt(argv: string[]): number | Promise<number> {
|
|
4101
|
+
const trusted = takeTrust(argv)
|
|
4102
|
+
if (null == trusted) {
|
|
4103
|
+
return 2
|
|
4104
|
+
}
|
|
4105
|
+
argv = trusted.argv
|
|
4106
|
+
const trust = trusted.trust
|
|
4566
4107
|
const files: string[] = []
|
|
4108
|
+
const profileFiles: string[] = []
|
|
4567
4109
|
let marker: string | undefined = undefined
|
|
4568
4110
|
const flags: FmtFlags = {
|
|
4569
4111
|
write: false, list: false, check: false, diff: false, lint: false, strict: false,
|
|
@@ -4604,6 +4146,14 @@ function runFmt(argv: string[]): number | Promise<number> {
|
|
|
4604
4146
|
return 2
|
|
4605
4147
|
}
|
|
4606
4148
|
}
|
|
4149
|
+
else if ('--profile' === arg) {
|
|
4150
|
+
const pf = argv[++i]
|
|
4151
|
+
if (null == pf) {
|
|
4152
|
+
process.stderr.write('aontu: --profile needs a file\n')
|
|
4153
|
+
return 2
|
|
4154
|
+
}
|
|
4155
|
+
profileFiles.push(pf)
|
|
4156
|
+
}
|
|
4607
4157
|
else if (arg.startsWith('-')) {
|
|
4608
4158
|
process.stderr.write(`aontu: unknown fmt option ${arg} (try --help)\n`)
|
|
4609
4159
|
return 2
|
|
@@ -4629,6 +4179,11 @@ function runFmt(argv: string[]): number | Promise<number> {
|
|
|
4629
4179
|
})
|
|
4630
4180
|
}
|
|
4631
4181
|
|
|
4182
|
+
const declared = loadProfiles(profileFiles, trust)
|
|
4183
|
+
if ('number' === typeof declared) {
|
|
4184
|
+
return declared
|
|
4185
|
+
}
|
|
4186
|
+
|
|
4632
4187
|
// Several files onto standard output would be one stream nobody can
|
|
4633
4188
|
// split again (the note's X-6): the verb refuses unless an option
|
|
4634
4189
|
// says what to do with each.
|
|
@@ -4649,13 +4204,14 @@ function runFmt(argv: string[]): number | Promise<number> {
|
|
|
4649
4204
|
process.stderr.write(`aontu: cannot read ${err.path}: ${err.message}\n`)
|
|
4650
4205
|
return 2
|
|
4651
4206
|
}
|
|
4652
|
-
const mark = fmtMarker(file, src,
|
|
4207
|
+
const mark = fmtMarker(file, src,
|
|
4208
|
+
marker ?? markerFromProfiles(declared, file))
|
|
4653
4209
|
if (false === mark) {
|
|
4654
4210
|
process.stderr.write(
|
|
4655
4211
|
`aontu: ${file} is not aontu source (.aon, .aontu) and carries no ` +
|
|
4656
4212
|
`${markerFor(file)} marker line, so there is no aontu in it to ` +
|
|
4657
|
-
'format; --marker names the marker for a language the table
|
|
4658
|
-
'not know\n')
|
|
4213
|
+
'format; --marker names the marker for a language the table ' +
|
|
4214
|
+
'does not know, and --profile reads one that declares it\n')
|
|
4659
4215
|
return 2
|
|
4660
4216
|
}
|
|
4661
4217
|
worst = Math.max(worst, fmtOne(file, src, flags, mark))
|
|
@@ -4663,20 +4219,6 @@ function runFmt(argv: string[]): number | Promise<number> {
|
|
|
4663
4219
|
return worst
|
|
4664
4220
|
}
|
|
4665
4221
|
|
|
4666
|
-
// WHAT A FILE IS, BY ITS EXTENSION (ADR-012's rule, and the one
|
|
4667
|
-
// `render` reads an entry by): `.aon` and `.aontu` are aontu source,
|
|
4668
|
-
// and anything else is a GENERATOR written in the target's own syntax
|
|
4669
|
-
// (docs/design/TEMPLATE.0.md), whose marker lines carry the document
|
|
4670
|
-
// this formats and whose other lines are output. `undefined` is aontu,
|
|
4671
|
-
// a string is the generator's marker, and `false` is neither.
|
|
4672
|
-
//
|
|
4673
|
-
// A FILE WITH NO MARKER LINE IN IT IS NEITHER, and that is what keeps
|
|
4674
|
-
// FMT.0.md §9's boundary where it stood: a `.json`, `.yaml` or `.toml`
|
|
4675
|
-
// include is another language's file, and reading one as a generator
|
|
4676
|
-
// would answer it back unchanged having understood none of it. The
|
|
4677
|
-
// marker is the evidence that a file was written to carry aontu at
|
|
4678
|
-
// all. `--marker` says so outright, and then the file is a generator
|
|
4679
|
-
// whatever it is called.
|
|
4680
4222
|
function fmtMarker(
|
|
4681
4223
|
file: string, src: string, marker: string | undefined): string | undefined | false {
|
|
4682
4224
|
if (undefined !== marker) {
|
|
@@ -4738,26 +4280,13 @@ function fmtOne(
|
|
|
4738
4280
|
}
|
|
4739
4281
|
|
|
4740
4282
|
|
|
4741
|
-
// ---------------------------------------------------------------------
|
|
4742
|
-
// The servers as verbs: `aontu lsp` runs the language server and
|
|
4743
|
-
// `aontu mcp` the MCP server over the CLI's own streams, so the one
|
|
4744
|
-
// command on PATH is the editor's and the agent's server too, and a
|
|
4745
|
-
// version manager (docs/design/ENV.0.md) has one thing to resolve. The
|
|
4746
|
-
// standalone bins (aontu-lsp, aontu-mcp) run the same functions. Each
|
|
4747
|
-
// server owns its exit; the CLI only dispatches. The pair is a
|
|
4748
|
-
// parameter of main so that a test can see the dispatch without a
|
|
4749
|
-
// server taking the test's own stdin.
|
|
4750
|
-
|
|
4751
4283
|
type Servers = {
|
|
4752
4284
|
lsp: () => void
|
|
4753
4285
|
mcp: (argv: string[]) => void
|
|
4754
4286
|
}
|
|
4755
4287
|
|
|
4756
|
-
//
|
|
4757
|
-
//
|
|
4758
|
-
// cli.test.ts run each through a child process instead, so these two
|
|
4759
|
-
// lines are excluded from the in-process count, as the stdio wiring
|
|
4760
|
-
// of lsp-server.ts is.
|
|
4288
|
+
// Excluded: the real pair takes the process stdio, so ts/test/cli.test.ts
|
|
4289
|
+
// drives each server through a child process instead.
|
|
4761
4290
|
/* node:coverage ignore next 4 */
|
|
4762
4291
|
const SERVERS: Servers = {
|
|
4763
4292
|
lsp: () => void lspMain(),
|
|
@@ -4804,20 +4333,6 @@ function parseTrustArg(value: string): TrustArg | undefined {
|
|
|
4804
4333
|
}
|
|
4805
4334
|
|
|
4806
4335
|
|
|
4807
|
-
// THE TEACHING PACK, SERVED FROM THE COMMAND (G11 phase 1,
|
|
4808
|
-
// docs/capability-review/g11-agent-onramp.md; mirrors
|
|
4809
|
-
// go/cmd/aontu/help.go).
|
|
4810
|
-
//
|
|
4811
|
-
// HELP documents the TOOLCHAIN and says nothing about the LANGUAGE:
|
|
4812
|
-
// `&`, the map template and the one construct an ontology cannot be
|
|
4813
|
-
// written without, occurs zero times in it, while `template` occurs
|
|
4814
|
-
// fourteen times and names an unrelated verb every time. docs/skill/
|
|
4815
|
-
// was already the right content and already gated; the gap was
|
|
4816
|
-
// DELIVERY, since it reached an installation as
|
|
4817
|
-
// node_modules/aontu/skill/ where nothing looks. ts/src/helpdoc.ts is
|
|
4818
|
-
// generated from those sources by ts/scripts/helpdoc.cjs and asserted
|
|
4819
|
-
// byte-identical with them by ts/test/helpdoc.test.ts.
|
|
4820
|
-
|
|
4821
4336
|
const HELP_VERB_HELP = 'aontu help [topic] (try `aontu help` for the topics)'
|
|
4822
4337
|
const EXPLAIN_HELP = 'aontu explain <code> (try `aontu explain --list`)'
|
|
4823
4338
|
|
|
@@ -4903,23 +4418,6 @@ function runHelp(argv: string[]): number {
|
|
|
4903
4418
|
}
|
|
4904
4419
|
|
|
4905
4420
|
|
|
4906
|
-
// `aontu explain <code>` (G11 phase 3; mirrors
|
|
4907
|
-
// go/cmd/aontu/explain.go).
|
|
4908
|
-
//
|
|
4909
|
-
// THE REGISTRY IS THE LIST, NOT THE HINT TABLE. test/spec/errcodes.tsv
|
|
4910
|
-
// registers 157 codes and the spec suite asserts set equality between
|
|
4911
|
-
// the file and codeClasses IN BOTH PORTS, so listing from codeClasses
|
|
4912
|
-
// is listing the shared contract. The hint tables are smaller and are
|
|
4913
|
-
// NOT in parity -- 130 entries here against 131 in Go, the extra being
|
|
4914
|
-
// decimal_syntax, which this port never raises -- so listing from them
|
|
4915
|
-
// would make `aontu explain --list` differ between ports over a
|
|
4916
|
-
// difference that is not about what either port can report.
|
|
4917
|
-
//
|
|
4918
|
-
// A REGISTERED CODE WITH NO HINT ANSWERS WITH ITS CLASS AND SAYS SO.
|
|
4919
|
-
// Twenty-seven registered codes carry no explanation text here; before
|
|
4920
|
-
// this verb their absence was invisible, because a hint is only ever
|
|
4921
|
-
// seen beside the error that raises it.
|
|
4922
|
-
|
|
4923
4421
|
// The dynamic prefixes a generated code extends (`func:upper`,
|
|
4924
4422
|
// `op[+]`). Mirrors CODE_PREFIXES in ts/src/hints.ts, which is not
|
|
4925
4423
|
// exported; a code that extends one is registered through its prefix
|
|
@@ -5062,21 +4560,6 @@ function runExplain(argv: string[]): number {
|
|
|
5062
4560
|
}
|
|
5063
4561
|
|
|
5064
4562
|
|
|
5065
|
-
// `aontu init` (G11 phase 6,
|
|
5066
|
-
// docs/capability-review/g11-agent-onramp.md).
|
|
5067
|
-
//
|
|
5068
|
-
// NOT SCAFFOLDING CONVENIENCE. The agent's most expensive failure is
|
|
5069
|
-
// writing a FIRST document at all: the measurement that opened G11
|
|
5070
|
-
// found one reaching for the wildcard its neighbours use and getting
|
|
5071
|
-
// `verdict: valid` over data that violates it. A known-good starting
|
|
5072
|
-
// document turns generation into editing, which is the operation a
|
|
5073
|
-
// model is reliably good at.
|
|
5074
|
-
//
|
|
5075
|
-
// The trio is real, runnable and tested where it lives
|
|
5076
|
-
// (docs/skill/init/, run by ts/test/helpdoc.test.ts), and staged into
|
|
5077
|
-
// both ports by the same generator that stages the teaching pack, so
|
|
5078
|
-
// the two write the same bytes.
|
|
5079
|
-
|
|
5080
4563
|
const INIT_HELP = 'aontu init [dir] (try --help)'
|
|
5081
4564
|
|
|
5082
4565
|
|
|
@@ -5101,10 +4584,6 @@ function runInit(argv: string[]): number {
|
|
|
5101
4584
|
}
|
|
5102
4585
|
const dir = dirs[0] ?? '.'
|
|
5103
4586
|
|
|
5104
|
-
// REFUSES TO OVERWRITE, and checks every member BEFORE writing any of
|
|
5105
|
-
// them: a scaffold that wrote two files and then refused the third
|
|
5106
|
-
// would leave a directory in a state neither the caller nor a re-run
|
|
5107
|
-
// can reason about.
|
|
5108
4587
|
const standing = INITDOC.filter((f) => existsSync(join(dir, f.name)))
|
|
5109
4588
|
if (0 < standing.length) {
|
|
5110
4589
|
process.stderr.write(
|
|
@@ -5134,12 +4613,6 @@ function runInit(argv: string[]): number {
|
|
|
5134
4613
|
}
|
|
5135
4614
|
|
|
5136
4615
|
|
|
5137
|
-
// EVERY VERB THIS PORT DISPATCHES, for the nearest-verb suggestion
|
|
5138
|
-
// G11 phase 2 prints. A separate list from the if-chain in main()
|
|
5139
|
-
// because the chain's arms have three different shapes and cannot be
|
|
5140
|
-
// a table; ts/test/cli-help.test.ts keeps the two from drifting by
|
|
5141
|
-
// running each name and requiring it not to fall through to the bare
|
|
5142
|
-
// command.
|
|
5143
4616
|
const KNOWN_VERBS = [
|
|
5144
4617
|
'agentsmd', 'allow', 'breaking', 'explain', 'fmt', 'get', 'hash',
|
|
5145
4618
|
'help', 'init', 'jsonschema', 'lsp', 'mcp', 'mod', 'reaches',
|
|
@@ -5159,13 +4632,6 @@ function looksLikeVerb(arg: string): boolean {
|
|
|
5159
4632
|
}
|
|
5160
4633
|
|
|
5161
4634
|
|
|
5162
|
-
// NEAREST-VERB SUGGESTION (G11 phase 2). Restricted
|
|
5163
|
-
// Damerau-Levenshtein with a cap that grows with the word and stops at
|
|
5164
|
-
// three: one edit is a convincing suggestion on any length, three is
|
|
5165
|
-
// the most that can be believed on a long one, and an UNCAPPED
|
|
5166
|
-
// nearest match on a three-letter typo names something unrelated with
|
|
5167
|
-
// confidence. Mirrors go/cmd/aontu/help.go, including the sort, so
|
|
5168
|
-
// the two ports suggest the same verb on a tie.
|
|
5169
4635
|
function nearestVerb(word: string, verbs: string[]): string {
|
|
5170
4636
|
let best = ''
|
|
5171
4637
|
let bestDist = Infinity
|
|
@@ -5181,8 +4647,6 @@ function nearestVerb(word: string, verbs: string[]): string {
|
|
|
5181
4647
|
}
|
|
5182
4648
|
|
|
5183
4649
|
|
|
5184
|
-
// Levenshtein with a transposition, iterative over two rows. Mirrors
|
|
5185
|
-
// editDistance in go/cmd/aontu/help.go exactly.
|
|
5186
4650
|
function editDistance(a: string, b: string): number {
|
|
5187
4651
|
const ar = [...a]
|
|
5188
4652
|
const br = [...b]
|
|
@@ -5209,12 +4673,6 @@ function editDistance(a: string, b: string): number {
|
|
|
5209
4673
|
|
|
5210
4674
|
|
|
5211
4675
|
function main(argv: string[], servers: Servers = SERVERS): void {
|
|
5212
|
-
// COLOUR OFF WHEN THE DESTINATION IS NOT A TERMINAL. Error frames
|
|
5213
|
-
// hardcoded their ANSI escapes, so a piped report and a `--jsonl`
|
|
5214
|
-
// answer carried terminal control codes into whatever read them (the
|
|
5215
|
-
// review's finding F). `NO_COLOR` is honoured by the library itself;
|
|
5216
|
-
// only the CLI can see whether its stderr is a terminal, so only the
|
|
5217
|
-
// CLI can make this call. `undefined` means "leave it to NO_COLOR".
|
|
5218
4676
|
setColor(true === process.stderr.isTTY ? undefined : false)
|
|
5219
4677
|
|
|
5220
4678
|
let mode: Mode = 'json'
|
|
@@ -5222,13 +4680,6 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
5222
4680
|
// reads exactly what it always read, and a caller that asks for json
|
|
5223
4681
|
// gets the failure in the finding shape every other verb reports.
|
|
5224
4682
|
let format: EvalFormat = 'text'
|
|
5225
|
-
// A LIST, though the bare command evaluates exactly one document.
|
|
5226
|
-
// It used to be one variable and the last argument won, which made a
|
|
5227
|
-
// MISTYPED VERB a silent success: `aontu vet2 schema.aon good.json`
|
|
5228
|
-
// printed good.json and exited 0, because `vet2` matched no
|
|
5229
|
-
// subcommand, fell through to this loop as a file name, and was
|
|
5230
|
-
// overwritten twice. In a tool loop that reads as a passing
|
|
5231
|
-
// validation. Counting them is what lets the refusal below happen.
|
|
5232
4683
|
const files: string[] = []
|
|
5233
4684
|
let trust: TrustArg = { kind: 'system-warn', textExt: [] }
|
|
5234
4685
|
let textExt: string[] = []
|
|
@@ -5238,15 +4689,6 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
5238
4689
|
// mode the REPL already has.
|
|
5239
4690
|
let jsonl = false
|
|
5240
4691
|
|
|
5241
|
-
// Subcommand dispatch, and deliberately only for a FIRST argument:
|
|
5242
|
-
// `aontu vet` is the verb, while `aontu somefile vet` keeps meaning
|
|
5243
|
-
// what it always did. A file named `vet` is still reachable as
|
|
5244
|
-
// `aontu ./vet`.
|
|
5245
|
-
//
|
|
5246
|
-
// Promise.resolve either way: a non-watch run returns its exit class
|
|
5247
|
-
// synchronously (and has already written its report), while `--watch`
|
|
5248
|
-
// resolves only when the watch ends — so one await-shaped line serves
|
|
5249
|
-
// both without a branch to keep covered.
|
|
5250
4692
|
if ('vet' === argv[2]) {
|
|
5251
4693
|
return void Promise.resolve(runVet(argv.slice(3))).then(finish)
|
|
5252
4694
|
}
|
|
@@ -5403,14 +4845,6 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
5403
4845
|
}
|
|
5404
4846
|
}
|
|
5405
4847
|
|
|
5406
|
-
// ONE DOCUMENT. The bare form has always been `aontu [options]
|
|
5407
|
-
// [file]`, singular, and anything past the first was silently
|
|
5408
|
-
// discarded rather than refused -- so every way of getting the verb
|
|
5409
|
-
// wrong (a typo, a verb this port does not have, a verb spelled for
|
|
5410
|
-
// another tool) ended in a plausible answer about the wrong file.
|
|
5411
|
-
// Exit 2, the usage class, and the message names the cause rather
|
|
5412
|
-
// than the symptom: nothing here can tell a mistyped verb from a
|
|
5413
|
-
// second file, but the reader can.
|
|
5414
4848
|
if (1 < files.length) {
|
|
5415
4849
|
process.stderr.write(
|
|
5416
4850
|
`aontu: the bare command evaluates one document, and ${files.length}` +
|
|
@@ -5419,9 +4853,6 @@ function main(argv: string[], servers: Servers = SERVERS): void {
|
|
|
5419
4853
|
return finish(2)
|
|
5420
4854
|
}
|
|
5421
4855
|
|
|
5422
|
-
// The extensions ride with the capability from here on, so the three
|
|
5423
|
-
// entry shapes below (file, REPL, stdin) each get them by threading
|
|
5424
|
-
// the one value they already thread.
|
|
5425
4856
|
trust = { ...trust, textExt }
|
|
5426
4857
|
|
|
5427
4858
|
const file = files[0]
|