perttool 0.1.0 → 0.3.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/CHANGELOG.md +112 -33
- package/README.md +206 -111
- package/dist/analysis/temporal-deadline.d.ts +59 -0
- package/dist/analysis/temporal-deadline.d.ts.map +1 -0
- package/dist/analysis/temporal-deadline.js +387 -0
- package/dist/analysis/temporal-deadline.js.map +1 -0
- package/dist/analysis/temporal-precedence.d.ts +45 -0
- package/dist/analysis/temporal-precedence.d.ts.map +1 -0
- package/dist/analysis/temporal-precedence.js +123 -0
- package/dist/analysis/temporal-precedence.js.map +1 -0
- package/dist/analysis/temporal-resource.d.ts +66 -0
- package/dist/analysis/temporal-resource.d.ts.map +1 -0
- package/dist/analysis/temporal-resource.js +292 -0
- package/dist/analysis/temporal-resource.js.map +1 -0
- package/dist/application/analyze.js +5 -5
- package/dist/application/analyze.js.map +1 -1
- package/dist/application/check.d.ts +16 -0
- package/dist/application/check.d.ts.map +1 -1
- package/dist/application/check.js +51 -0
- package/dist/application/check.js.map +1 -1
- package/dist/application/contract4.d.ts +17 -0
- package/dist/application/contract4.d.ts.map +1 -0
- package/dist/application/contract4.js +27 -0
- package/dist/application/contract4.js.map +1 -0
- package/dist/application/init.d.ts +43 -0
- package/dist/application/init.d.ts.map +1 -0
- package/dist/application/init.js +226 -0
- package/dist/application/init.js.map +1 -0
- package/dist/application/mutate.d.ts +19 -0
- package/dist/application/mutate.d.ts.map +1 -1
- package/dist/application/mutate.js +47 -25
- package/dist/application/mutate.js.map +1 -1
- package/dist/application/next.d.ts +2 -0
- package/dist/application/next.d.ts.map +1 -1
- package/dist/application/next.js +7 -1
- package/dist/application/next.js.map +1 -1
- package/dist/application/project.d.ts +3 -1
- package/dist/application/project.d.ts.map +1 -1
- package/dist/application/project.js +2 -1
- package/dist/application/project.js.map +1 -1
- package/dist/application/target-check.d.ts +34 -0
- package/dist/application/target-check.d.ts.map +1 -0
- package/dist/application/target-check.js +90 -0
- package/dist/application/target-check.js.map +1 -0
- package/dist/application/target-grammar-boundary.d.ts +22 -0
- package/dist/application/target-grammar-boundary.d.ts.map +1 -0
- package/dist/application/target-grammar-boundary.js +91 -0
- package/dist/application/target-grammar-boundary.js.map +1 -0
- package/dist/application/target-mutate.d.ts +8 -0
- package/dist/application/target-mutate.d.ts.map +1 -0
- package/dist/application/target-mutate.js +56 -0
- package/dist/application/target-mutate.js.map +1 -0
- package/dist/application/target-project.d.ts +28 -0
- package/dist/application/target-project.d.ts.map +1 -0
- package/dist/application/target-project.js +60 -0
- package/dist/application/target-project.js.map +1 -0
- package/dist/application/target-temporal-analysis.d.ts +138 -0
- package/dist/application/target-temporal-analysis.d.ts.map +1 -0
- package/dist/application/target-temporal-analysis.js +409 -0
- package/dist/application/target-temporal-analysis.js.map +1 -0
- package/dist/application/target-temporal-input.d.ts +86 -0
- package/dist/application/target-temporal-input.d.ts.map +1 -0
- package/dist/application/target-temporal-input.js +238 -0
- package/dist/application/target-temporal-input.js.map +1 -0
- package/dist/application/target-unit-migration-candidate.d.ts +60 -0
- package/dist/application/target-unit-migration-candidate.d.ts.map +1 -0
- package/dist/application/target-unit-migration-candidate.js +290 -0
- package/dist/application/target-unit-migration-candidate.js.map +1 -0
- package/dist/application/target-unit-migration-request.d.ts +35 -0
- package/dist/application/target-unit-migration-request.d.ts.map +1 -0
- package/dist/application/target-unit-migration-request.js +68 -0
- package/dist/application/target-unit-migration-request.js.map +1 -0
- package/dist/application/target-unit-migration-result.d.ts +66 -0
- package/dist/application/target-unit-migration-result.d.ts.map +1 -0
- package/dist/application/target-unit-migration-result.js +86 -0
- package/dist/application/target-unit-migration-result.js.map +1 -0
- package/dist/application/unit-migration.d.ts +13 -0
- package/dist/application/unit-migration.d.ts.map +1 -0
- package/dist/application/unit-migration.js +9 -0
- package/dist/application/unit-migration.js.map +1 -0
- package/dist/cli.js +494 -383
- package/dist/cli.js.map +1 -1
- package/dist/command/discovery.d.ts +30 -0
- package/dist/command/discovery.d.ts.map +1 -0
- package/dist/command/discovery.js +752 -0
- package/dist/command/discovery.js.map +1 -0
- package/dist/command/registry.d.ts +84 -0
- package/dist/command/registry.d.ts.map +1 -0
- package/dist/command/registry.js +964 -0
- package/dist/command/registry.js.map +1 -0
- package/dist/command/usage.d.ts +42 -0
- package/dist/command/usage.d.ts.map +1 -0
- package/dist/command/usage.js +337 -0
- package/dist/command/usage.js.map +1 -0
- package/dist/conversion/mermaid-import.js +56 -56
- package/dist/conversion/mermaid-import.js.map +1 -1
- package/dist/conversion/mermaid.js +1 -1
- package/dist/conversion/mermaid.js.map +1 -1
- package/dist/formatter/source-formatter.d.ts +12 -0
- package/dist/formatter/source-formatter.d.ts.map +1 -1
- package/dist/formatter/source-formatter.js +48 -6
- package/dist/formatter/source-formatter.js.map +1 -1
- package/dist/formatter/target-source-formatter.d.ts +8 -0
- package/dist/formatter/target-source-formatter.d.ts.map +1 -0
- package/dist/formatter/target-source-formatter.js +36 -0
- package/dist/formatter/target-source-formatter.js.map +1 -0
- package/dist/help/guide.d.ts +12 -0
- package/dist/help/guide.d.ts.map +1 -0
- package/dist/help/guide.js +103 -0
- package/dist/help/guide.js.map +1 -0
- package/dist/help/registry.d.ts +0 -6
- package/dist/help/registry.d.ts.map +1 -1
- package/dist/help/registry.js +186 -86
- package/dist/help/registry.js.map +1 -1
- package/dist/index.d.ts +20 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/io/safe-write.d.ts +7 -0
- package/dist/io/safe-write.d.ts.map +1 -1
- package/dist/io/safe-write.js +36 -27
- package/dist/io/safe-write.js.map +1 -1
- package/dist/io/target-safe-write.d.ts +7 -0
- package/dist/io/target-safe-write.d.ts.map +1 -0
- package/dist/io/target-safe-write.js +33 -0
- package/dist/io/target-safe-write.js.map +1 -0
- package/dist/migration/conversion.d.ts +40 -0
- package/dist/migration/conversion.d.ts.map +1 -0
- package/dist/migration/conversion.js +106 -0
- package/dist/migration/conversion.js.map +1 -0
- package/dist/migration/grammar-boundary.d.ts +20 -0
- package/dist/migration/grammar-boundary.d.ts.map +1 -0
- package/dist/migration/grammar-boundary.js +92 -0
- package/dist/migration/grammar-boundary.js.map +1 -0
- package/dist/migration/request.d.ts +79 -0
- package/dist/migration/request.d.ts.map +1 -0
- package/dist/migration/request.js +308 -0
- package/dist/migration/request.js.map +1 -0
- package/dist/model/calendar-arithmetic.d.ts +46 -0
- package/dist/model/calendar-arithmetic.d.ts.map +1 -0
- package/dist/model/calendar-arithmetic.js +236 -0
- package/dist/model/calendar-arithmetic.js.map +1 -0
- package/dist/model/calendar.d.ts +22 -0
- package/dist/model/calendar.d.ts.map +1 -0
- package/dist/model/calendar.js +83 -0
- package/dist/model/calendar.js.map +1 -0
- package/dist/model/declaration-fields.d.ts +15 -0
- package/dist/model/declaration-fields.d.ts.map +1 -0
- package/dist/model/declaration-fields.js +54 -0
- package/dist/model/declaration-fields.js.map +1 -0
- package/dist/model/exact-duration-source.d.ts +10 -0
- package/dist/model/exact-duration-source.d.ts.map +1 -0
- package/dist/model/exact-duration-source.js +69 -0
- package/dist/model/exact-duration-source.js.map +1 -0
- package/dist/model/rational.d.ts +2 -2
- package/dist/model/rational.d.ts.map +1 -1
- package/dist/model/rational.js +3 -1
- package/dist/model/rational.js.map +1 -1
- package/dist/model/syntax.d.ts +9 -2
- package/dist/model/syntax.d.ts.map +1 -1
- package/dist/model/syntax.js +20 -4
- package/dist/model/syntax.js.map +1 -1
- package/dist/model/target-calendar.d.ts +25 -0
- package/dist/model/target-calendar.d.ts.map +1 -0
- package/dist/model/target-calendar.js +56 -0
- package/dist/model/target-calendar.js.map +1 -0
- package/dist/mutation/advance.d.ts.map +1 -1
- package/dist/mutation/advance.js +21 -4
- package/dist/mutation/advance.js.map +1 -1
- package/dist/mutation/gate.d.ts +5 -0
- package/dist/mutation/gate.d.ts.map +1 -0
- package/dist/mutation/gate.js +156 -0
- package/dist/mutation/gate.js.map +1 -0
- package/dist/mutation/milestone.d.ts +10 -1
- package/dist/mutation/milestone.d.ts.map +1 -1
- package/dist/mutation/milestone.js +104 -48
- package/dist/mutation/milestone.js.map +1 -1
- package/dist/mutation/project.d.ts +7 -1
- package/dist/mutation/project.d.ts.map +1 -1
- package/dist/mutation/project.js +32 -19
- package/dist/mutation/project.js.map +1 -1
- package/dist/mutation/resource.js +22 -22
- package/dist/mutation/resource.js.map +1 -1
- package/dist/mutation/target-types.d.ts +42 -0
- package/dist/mutation/target-types.d.ts.map +1 -0
- package/dist/mutation/target-types.js +2 -0
- package/dist/mutation/target-types.js.map +1 -0
- package/dist/mutation/task.d.ts +12 -1
- package/dist/mutation/task.d.ts.map +1 -1
- package/dist/mutation/task.js +148 -89
- package/dist/mutation/task.js.map +1 -1
- package/dist/mutation/types.d.ts +34 -3
- package/dist/mutation/types.d.ts.map +1 -1
- package/dist/parser/document-parser.d.ts +14 -0
- package/dist/parser/document-parser.d.ts.map +1 -1
- package/dist/parser/document-parser.js +159 -55
- package/dist/parser/document-parser.js.map +1 -1
- package/dist/recommendation/override-types.d.ts +2 -2
- package/dist/recommendation/override.d.ts +2 -2
- package/dist/recommendation/override.d.ts.map +1 -1
- package/dist/recommendation/override.js +49 -12
- package/dist/recommendation/override.js.map +1 -1
- package/dist/semantic/target-validator.d.ts +36 -0
- package/dist/semantic/target-validator.d.ts.map +1 -0
- package/dist/semantic/target-validator.js +87 -0
- package/dist/semantic/target-validator.js.map +1 -0
- package/dist/semantic/validator.d.ts +2 -0
- package/dist/semantic/validator.d.ts.map +1 -1
- package/dist/semantic/validator.js +90 -45
- package/dist/semantic/validator.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
import { TOOL_VERSION } from "../version.js";
|
|
2
|
+
import { COMMAND_REGISTRY, commandDescriptorToJson, } from "./registry.js";
|
|
3
|
+
const contract3Mappings = Object.freeze({
|
|
4
|
+
"dsl.check": {
|
|
5
|
+
path: ["document", "check"],
|
|
6
|
+
operation: "document.check",
|
|
7
|
+
},
|
|
8
|
+
"dsl.format": {
|
|
9
|
+
path: ["document", "format"],
|
|
10
|
+
operation: "document.format",
|
|
11
|
+
},
|
|
12
|
+
"dsl.help": {
|
|
13
|
+
path: ["guide"],
|
|
14
|
+
operation: "guide",
|
|
15
|
+
resultSchemaReplacements: {
|
|
16
|
+
"Perttool.HelpResult.v1": "Perttool.GuideResult.v1",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
"mutation.apply": {
|
|
20
|
+
path: ["batch", "apply"],
|
|
21
|
+
operation: "batch.apply",
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
const resourceDefinitions = Object.freeze([
|
|
25
|
+
Object.freeze({
|
|
26
|
+
name: "document",
|
|
27
|
+
summary: "Validate and source-format PERT documents.",
|
|
28
|
+
actionOrder: Object.freeze(["check", "format"]),
|
|
29
|
+
}),
|
|
30
|
+
Object.freeze({
|
|
31
|
+
name: "project",
|
|
32
|
+
summary: "Inspect and maintain effective project metadata.",
|
|
33
|
+
actionOrder: Object.freeze(["init", "show", "set", "migrate-unit"]),
|
|
34
|
+
}),
|
|
35
|
+
Object.freeze({
|
|
36
|
+
name: "dag",
|
|
37
|
+
summary: "Analyze, select, advance, and convert the project graph.",
|
|
38
|
+
actionOrder: Object.freeze(["analyze", "next", "advance", "render", "import"]),
|
|
39
|
+
}),
|
|
40
|
+
Object.freeze({
|
|
41
|
+
name: "task",
|
|
42
|
+
summary: "Maintain task edges through source-preserving previews.",
|
|
43
|
+
actionOrder: Object.freeze(["add", "set", "remove", "finish"]),
|
|
44
|
+
}),
|
|
45
|
+
Object.freeze({
|
|
46
|
+
name: "gate",
|
|
47
|
+
summary: "Maintain zero-duration dependency edges through source-preserving previews.",
|
|
48
|
+
actionOrder: Object.freeze(["add", "set", "remove"]),
|
|
49
|
+
}),
|
|
50
|
+
Object.freeze({
|
|
51
|
+
name: "milestone",
|
|
52
|
+
summary: "Maintain milestone nodes through source-preserving previews.",
|
|
53
|
+
actionOrder: Object.freeze(["add", "set", "remove"]),
|
|
54
|
+
}),
|
|
55
|
+
Object.freeze({
|
|
56
|
+
name: "resource",
|
|
57
|
+
summary: "Maintain resource capacities through source-preserving previews.",
|
|
58
|
+
actionOrder: Object.freeze(["add", "set", "remove"]),
|
|
59
|
+
}),
|
|
60
|
+
Object.freeze({
|
|
61
|
+
name: "batch",
|
|
62
|
+
summary: "Apply one typed atomic mutation request as a preview.",
|
|
63
|
+
actionOrder: Object.freeze(["apply"]),
|
|
64
|
+
}),
|
|
65
|
+
Object.freeze({
|
|
66
|
+
name: "agent",
|
|
67
|
+
summary: "Inspect bundled offline AI agent guidance.",
|
|
68
|
+
actionOrder: Object.freeze(["help"]),
|
|
69
|
+
}),
|
|
70
|
+
]);
|
|
71
|
+
const helpDescriptor = Object.freeze({
|
|
72
|
+
contractVersion: 4,
|
|
73
|
+
path: Object.freeze(["help"]),
|
|
74
|
+
operation: "help",
|
|
75
|
+
summary: "Discovers the complete implemented command contract.",
|
|
76
|
+
operands: Object.freeze([
|
|
77
|
+
Object.freeze({
|
|
78
|
+
name: "resource",
|
|
79
|
+
valueType: "command-resource",
|
|
80
|
+
required: false,
|
|
81
|
+
position: 0,
|
|
82
|
+
}),
|
|
83
|
+
Object.freeze({
|
|
84
|
+
name: "action",
|
|
85
|
+
valueType: "command-action",
|
|
86
|
+
required: false,
|
|
87
|
+
position: 1,
|
|
88
|
+
}),
|
|
89
|
+
]),
|
|
90
|
+
options: Object.freeze([
|
|
91
|
+
Object.freeze({
|
|
92
|
+
name: "format",
|
|
93
|
+
kind: "value",
|
|
94
|
+
valueType: "output-format",
|
|
95
|
+
required: false,
|
|
96
|
+
repeatable: false,
|
|
97
|
+
defaultValue: "text",
|
|
98
|
+
enumValues: Object.freeze(["text", "json"]),
|
|
99
|
+
conflicts: Object.freeze([]),
|
|
100
|
+
requires: Object.freeze([]),
|
|
101
|
+
sharedGroup: null,
|
|
102
|
+
spelling: Object.freeze({
|
|
103
|
+
cli: "--format",
|
|
104
|
+
dsl: null,
|
|
105
|
+
json: "format",
|
|
106
|
+
}),
|
|
107
|
+
}),
|
|
108
|
+
]),
|
|
109
|
+
input: "none",
|
|
110
|
+
output: Object.freeze({
|
|
111
|
+
formats: Object.freeze(["text", "json"]),
|
|
112
|
+
payload: "result",
|
|
113
|
+
fileEffect: "none",
|
|
114
|
+
}),
|
|
115
|
+
stdin: Object.freeze({
|
|
116
|
+
document: false,
|
|
117
|
+
artifact: false,
|
|
118
|
+
request: false,
|
|
119
|
+
mutuallyExclusive: false,
|
|
120
|
+
}),
|
|
121
|
+
effect: "read",
|
|
122
|
+
resultSchemas: Object.freeze([
|
|
123
|
+
"Perttool.CommandHelpResult.v1",
|
|
124
|
+
"Perttool.CliError.v1",
|
|
125
|
+
]),
|
|
126
|
+
exitStatuses: Object.freeze([
|
|
127
|
+
Object.freeze({ code: 0, meaning: "Successful command lookup." }),
|
|
128
|
+
Object.freeze({ code: 1, meaning: "Command-help lookup failure." }),
|
|
129
|
+
Object.freeze({ code: 2, meaning: "CLI usage error." }),
|
|
130
|
+
Object.freeze({ code: 70, meaning: "Internal invariant or programmer error." }),
|
|
131
|
+
]),
|
|
132
|
+
examples: Object.freeze([
|
|
133
|
+
Object.freeze({
|
|
134
|
+
id: "catalog",
|
|
135
|
+
invocation: "perttool help --format json",
|
|
136
|
+
summary: "Return the complete implemented command catalog.",
|
|
137
|
+
}),
|
|
138
|
+
Object.freeze({
|
|
139
|
+
id: "resource",
|
|
140
|
+
invocation: "perttool help project --format json",
|
|
141
|
+
summary: "Return every project action.",
|
|
142
|
+
}),
|
|
143
|
+
Object.freeze({
|
|
144
|
+
id: "action",
|
|
145
|
+
invocation: "perttool help project show --format json",
|
|
146
|
+
summary: "Return the complete project show contract.",
|
|
147
|
+
}),
|
|
148
|
+
]),
|
|
149
|
+
});
|
|
150
|
+
function contract3Path(descriptor) {
|
|
151
|
+
return contract3Mappings[descriptor.operation] ?? {
|
|
152
|
+
path: descriptor.path,
|
|
153
|
+
operation: descriptor.operation,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function projectExample(example, sourcePath, targetPath) {
|
|
157
|
+
const sourcePrefix = `perttool ${sourcePath.join(" ")}`;
|
|
158
|
+
const targetPrefix = `perttool ${targetPath.join(" ")}`;
|
|
159
|
+
if (!example.invocation.startsWith(sourcePrefix)) {
|
|
160
|
+
throw new Error(`command example ${example.id} does not start with ${sourcePrefix}`);
|
|
161
|
+
}
|
|
162
|
+
return Object.freeze({
|
|
163
|
+
...example,
|
|
164
|
+
invocation: `${targetPrefix}${example.invocation.slice(sourcePrefix.length)}`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function projectContract3Descriptor(descriptor) {
|
|
168
|
+
const mapping = contract3Path(descriptor);
|
|
169
|
+
return Object.freeze({
|
|
170
|
+
contractVersion: 3,
|
|
171
|
+
path: Object.freeze([...mapping.path]),
|
|
172
|
+
operation: mapping.operation,
|
|
173
|
+
summary: descriptor.summary,
|
|
174
|
+
operands: descriptor.operands,
|
|
175
|
+
options: descriptor.options,
|
|
176
|
+
input: descriptor.input,
|
|
177
|
+
output: descriptor.output,
|
|
178
|
+
stdin: descriptor.stdin,
|
|
179
|
+
effect: descriptor.effect,
|
|
180
|
+
resultSchemas: Object.freeze(descriptor.resultSchemas.map((schema) => mapping.resultSchemaReplacements?.[schema] ?? schema)),
|
|
181
|
+
exitStatuses: descriptor.exitStatuses,
|
|
182
|
+
examples: Object.freeze(descriptor.examples.map((example) => projectExample(example, descriptor.path, mapping.path))),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function pathKey(path) {
|
|
186
|
+
return path.join("\0");
|
|
187
|
+
}
|
|
188
|
+
const projectedDescriptors = COMMAND_REGISTRY.map(projectContract3Descriptor);
|
|
189
|
+
const projectedMutationTemplate = projectedDescriptors.find(({ operation }) => operation === "resource.remove");
|
|
190
|
+
if (projectedMutationTemplate === undefined) {
|
|
191
|
+
throw new Error("Contract 4 mutation descriptor template is missing");
|
|
192
|
+
}
|
|
193
|
+
const mutationTemplate = projectedMutationTemplate;
|
|
194
|
+
function targetOperand(name, position, valueType) {
|
|
195
|
+
return Object.freeze({
|
|
196
|
+
name,
|
|
197
|
+
position,
|
|
198
|
+
valueType,
|
|
199
|
+
required: true,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function targetOption(name, required = false) {
|
|
203
|
+
return Object.freeze({
|
|
204
|
+
name,
|
|
205
|
+
kind: "value",
|
|
206
|
+
valueType: name === "reason" ? "text" : "milestone-id",
|
|
207
|
+
required,
|
|
208
|
+
repeatable: false,
|
|
209
|
+
defaultValue: null,
|
|
210
|
+
enumValues: Object.freeze([]),
|
|
211
|
+
conflicts: Object.freeze([]),
|
|
212
|
+
requires: Object.freeze([]),
|
|
213
|
+
sharedGroup: null,
|
|
214
|
+
spelling: Object.freeze({
|
|
215
|
+
cli: `--${name}`,
|
|
216
|
+
dsl: name,
|
|
217
|
+
json: name,
|
|
218
|
+
}),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
function targetValueOption(name, config) {
|
|
222
|
+
return Object.freeze({
|
|
223
|
+
name,
|
|
224
|
+
kind: "value",
|
|
225
|
+
valueType: config.valueType,
|
|
226
|
+
required: config.required ?? false,
|
|
227
|
+
repeatable: false,
|
|
228
|
+
defaultValue: config.defaultValue ?? null,
|
|
229
|
+
enumValues: Object.freeze([...(config.enumValues ?? [])]),
|
|
230
|
+
conflicts: Object.freeze([...(config.conflicts ?? [])]),
|
|
231
|
+
requires: Object.freeze([]),
|
|
232
|
+
sharedGroup: null,
|
|
233
|
+
spelling: Object.freeze({
|
|
234
|
+
cli: `--${name}`,
|
|
235
|
+
dsl: config.dsl ?? null,
|
|
236
|
+
json: name.replaceAll("-", "_"),
|
|
237
|
+
}),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function projectInitDescriptor() {
|
|
241
|
+
return Object.freeze({
|
|
242
|
+
contractVersion: 3,
|
|
243
|
+
path: Object.freeze(["project", "init"]),
|
|
244
|
+
operation: "project.init",
|
|
245
|
+
summary: "Previews the smallest valid project document or creates it exclusively.",
|
|
246
|
+
operands: Object.freeze([
|
|
247
|
+
targetOperand("project-id", 0, "project-id"),
|
|
248
|
+
]),
|
|
249
|
+
options: Object.freeze([
|
|
250
|
+
targetValueOption("title", {
|
|
251
|
+
valueType: "text",
|
|
252
|
+
required: true,
|
|
253
|
+
dsl: "title",
|
|
254
|
+
}),
|
|
255
|
+
targetValueOption("duration-unit", {
|
|
256
|
+
valueType: "duration-unit",
|
|
257
|
+
required: true,
|
|
258
|
+
enumValues: ["day", "hour", "point"],
|
|
259
|
+
dsl: "duration_unit",
|
|
260
|
+
}),
|
|
261
|
+
targetValueOption("initial-milestone", {
|
|
262
|
+
valueType: "milestone-id",
|
|
263
|
+
required: true,
|
|
264
|
+
}),
|
|
265
|
+
targetValueOption("initial-milestone-title", {
|
|
266
|
+
valueType: "text",
|
|
267
|
+
required: true,
|
|
268
|
+
}),
|
|
269
|
+
targetValueOption("finish", {
|
|
270
|
+
valueType: "milestone-id",
|
|
271
|
+
required: true,
|
|
272
|
+
dsl: "finish",
|
|
273
|
+
}),
|
|
274
|
+
targetValueOption("version", {
|
|
275
|
+
valueType: "integer",
|
|
276
|
+
defaultValue: 1,
|
|
277
|
+
dsl: "version",
|
|
278
|
+
}),
|
|
279
|
+
targetValueOption("as-of", {
|
|
280
|
+
valueType: "date-or-date-time",
|
|
281
|
+
dsl: "as_of",
|
|
282
|
+
}),
|
|
283
|
+
targetValueOption("velocity", {
|
|
284
|
+
valueType: "velocity",
|
|
285
|
+
dsl: "velocity",
|
|
286
|
+
}),
|
|
287
|
+
targetValueOption("out", {
|
|
288
|
+
valueType: "path",
|
|
289
|
+
}),
|
|
290
|
+
targetValueOption("format", {
|
|
291
|
+
valueType: "output-format",
|
|
292
|
+
defaultValue: "text",
|
|
293
|
+
enumValues: ["text", "json"],
|
|
294
|
+
}),
|
|
295
|
+
targetValueOption("color", {
|
|
296
|
+
valueType: "color-mode",
|
|
297
|
+
defaultValue: "auto",
|
|
298
|
+
enumValues: ["auto", "always", "never"],
|
|
299
|
+
conflicts: ["format=json when color=always"],
|
|
300
|
+
}),
|
|
301
|
+
]),
|
|
302
|
+
input: "none",
|
|
303
|
+
output: Object.freeze({
|
|
304
|
+
formats: Object.freeze(["text", "json"]),
|
|
305
|
+
payload: "candidate-document",
|
|
306
|
+
fileEffect: "optional-create",
|
|
307
|
+
}),
|
|
308
|
+
stdin: Object.freeze({
|
|
309
|
+
document: false,
|
|
310
|
+
artifact: false,
|
|
311
|
+
request: false,
|
|
312
|
+
mutuallyExclusive: false,
|
|
313
|
+
}),
|
|
314
|
+
effect: "write-or-create",
|
|
315
|
+
resultSchemas: Object.freeze([
|
|
316
|
+
"Perttool.InitResult.v1",
|
|
317
|
+
"Perttool.CliError.v1",
|
|
318
|
+
]),
|
|
319
|
+
exitStatuses: Object.freeze([
|
|
320
|
+
Object.freeze({ code: 0, meaning: "Successful preview or exclusive creation." }),
|
|
321
|
+
Object.freeze({ code: 1, meaning: "Candidate validation failure." }),
|
|
322
|
+
Object.freeze({ code: 2, meaning: "CLI usage error." }),
|
|
323
|
+
Object.freeze({ code: 3, meaning: "Output or encoding error." }),
|
|
324
|
+
Object.freeze({ code: 5, meaning: "Existing target, symlink, or creation conflict." }),
|
|
325
|
+
Object.freeze({ code: 70, meaning: "Internal invariant or programmer error." }),
|
|
326
|
+
]),
|
|
327
|
+
examples: Object.freeze([
|
|
328
|
+
Object.freeze({
|
|
329
|
+
id: "preview",
|
|
330
|
+
invocation: "perttool project init SAMPLE --title \"Sample project\" --duration-unit day --initial-milestone START --initial-milestone-title \"Project started\" --finish START",
|
|
331
|
+
summary: "Preview the complete smallest valid project document.",
|
|
332
|
+
}),
|
|
333
|
+
Object.freeze({
|
|
334
|
+
id: "out",
|
|
335
|
+
invocation: "perttool project init SAMPLE --title \"Sample project\" --duration-unit day --initial-milestone START --initial-milestone-title \"Project started\" --finish START --out plan.pert",
|
|
336
|
+
summary: "Create a new project document without overwriting a path.",
|
|
337
|
+
}),
|
|
338
|
+
]),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const gateMutationOperands = Object.freeze([
|
|
342
|
+
targetOperand("file", 0, "path-or-stdin"),
|
|
343
|
+
targetOperand("id", 1, "gate-id"),
|
|
344
|
+
]);
|
|
345
|
+
function gateDescriptor(action) {
|
|
346
|
+
const options = action === "add"
|
|
347
|
+
? [...mutationTemplate.options, targetOption("reason", true)]
|
|
348
|
+
: action === "set"
|
|
349
|
+
? [
|
|
350
|
+
...mutationTemplate.options,
|
|
351
|
+
targetOption("from"),
|
|
352
|
+
targetOption("to"),
|
|
353
|
+
targetOption("reason"),
|
|
354
|
+
]
|
|
355
|
+
: mutationTemplate.options;
|
|
356
|
+
const operands = action === "add"
|
|
357
|
+
? [
|
|
358
|
+
...gateMutationOperands,
|
|
359
|
+
targetOperand("from", 2, "milestone-id"),
|
|
360
|
+
targetOperand("to", 3, "milestone-id"),
|
|
361
|
+
]
|
|
362
|
+
: gateMutationOperands;
|
|
363
|
+
const detail = action === "add"
|
|
364
|
+
? "adding one gate"
|
|
365
|
+
: action === "set"
|
|
366
|
+
? "source-preserving gate endpoint or reason changes"
|
|
367
|
+
: "removing one gate without cascading";
|
|
368
|
+
const invocation = action === "add"
|
|
369
|
+
? "perttool gate add plan.pert APPROVAL READY DONE --reason \"Approval required\" --diff"
|
|
370
|
+
: action === "set"
|
|
371
|
+
? "perttool gate set plan.pert APPROVAL --reason \"Review accepted\" --diff"
|
|
372
|
+
: "perttool gate remove plan.pert APPROVAL --diff";
|
|
373
|
+
return Object.freeze({
|
|
374
|
+
contractVersion: 3,
|
|
375
|
+
path: Object.freeze(["gate", action]),
|
|
376
|
+
operation: `gate.${action}`,
|
|
377
|
+
summary: `Previews ${detail}.`,
|
|
378
|
+
operands: Object.freeze(operands),
|
|
379
|
+
options: Object.freeze(options),
|
|
380
|
+
input: mutationTemplate.input,
|
|
381
|
+
output: mutationTemplate.output,
|
|
382
|
+
stdin: mutationTemplate.stdin,
|
|
383
|
+
effect: mutationTemplate.effect,
|
|
384
|
+
resultSchemas: mutationTemplate.resultSchemas,
|
|
385
|
+
exitStatuses: mutationTemplate.exitStatuses,
|
|
386
|
+
examples: Object.freeze([
|
|
387
|
+
Object.freeze({
|
|
388
|
+
id: action,
|
|
389
|
+
invocation,
|
|
390
|
+
summary: `Preview gate ${action}.`,
|
|
391
|
+
}),
|
|
392
|
+
]),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
const targetOnlyDescriptors = Object.freeze([
|
|
396
|
+
projectInitDescriptor(),
|
|
397
|
+
gateDescriptor("add"),
|
|
398
|
+
gateDescriptor("set"),
|
|
399
|
+
gateDescriptor("remove"),
|
|
400
|
+
]);
|
|
401
|
+
const contract3Descriptors = Object.freeze([
|
|
402
|
+
...projectedDescriptors,
|
|
403
|
+
...targetOnlyDescriptors,
|
|
404
|
+
]);
|
|
405
|
+
const commonProjectionOptionNames = new Set([
|
|
406
|
+
"diff",
|
|
407
|
+
"write",
|
|
408
|
+
"expect-digest",
|
|
409
|
+
"out",
|
|
410
|
+
"max-diagnostics",
|
|
411
|
+
"warnings-as-errors",
|
|
412
|
+
"format",
|
|
413
|
+
"color",
|
|
414
|
+
]);
|
|
415
|
+
function insertedDomainOption(descriptor, option) {
|
|
416
|
+
if (descriptor.options.some(({ name }) => name === option.name)) {
|
|
417
|
+
throw new Error(`Contract 4 option --${option.name} already exists on ${descriptor.operation}`);
|
|
418
|
+
}
|
|
419
|
+
const insertionIndex = descriptor.options.findIndex(({ name }) => commonProjectionOptionNames.has(name));
|
|
420
|
+
if (insertionIndex < 0) {
|
|
421
|
+
return Object.freeze([...descriptor.options, option]);
|
|
422
|
+
}
|
|
423
|
+
return Object.freeze([
|
|
424
|
+
...descriptor.options.slice(0, insertionIndex),
|
|
425
|
+
option,
|
|
426
|
+
...descriptor.options.slice(insertionIndex),
|
|
427
|
+
]);
|
|
428
|
+
}
|
|
429
|
+
function contract4Options(descriptor) {
|
|
430
|
+
let options = descriptor.options;
|
|
431
|
+
const append = (option) => {
|
|
432
|
+
options = insertedDomainOption({ ...descriptor, options }, option);
|
|
433
|
+
};
|
|
434
|
+
if (descriptor.operation === "project.init") {
|
|
435
|
+
append(targetValueOption("initial-milestone-deadline", {
|
|
436
|
+
valueType: "date-or-date-time",
|
|
437
|
+
dsl: "milestone.deadline",
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
440
|
+
if (descriptor.operation === "task.add" || descriptor.operation === "task.set") {
|
|
441
|
+
append(targetValueOption("not-before", {
|
|
442
|
+
valueType: "date-or-date-time",
|
|
443
|
+
dsl: "not_before",
|
|
444
|
+
}));
|
|
445
|
+
append(targetValueOption("deadline", {
|
|
446
|
+
valueType: "date-or-date-time",
|
|
447
|
+
dsl: "deadline",
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
if (descriptor.operation === "milestone.add" || descriptor.operation === "milestone.set") {
|
|
451
|
+
append(targetValueOption("deadline", {
|
|
452
|
+
valueType: "date-or-date-time",
|
|
453
|
+
dsl: "deadline",
|
|
454
|
+
}));
|
|
455
|
+
}
|
|
456
|
+
if (descriptor.operation === "task.set" || descriptor.operation === "milestone.set") {
|
|
457
|
+
options = Object.freeze(options.map((option) => option.name !== "clear"
|
|
458
|
+
? option
|
|
459
|
+
: Object.freeze({
|
|
460
|
+
...option,
|
|
461
|
+
enumValues: Object.freeze([
|
|
462
|
+
...option.enumValues,
|
|
463
|
+
...(descriptor.operation === "task.set"
|
|
464
|
+
? ["not_before", "deadline"]
|
|
465
|
+
: ["deadline"]),
|
|
466
|
+
]),
|
|
467
|
+
})));
|
|
468
|
+
}
|
|
469
|
+
return options;
|
|
470
|
+
}
|
|
471
|
+
const contract4SchemaReplacements = Object.freeze({
|
|
472
|
+
"Perttool.CheckResult.v1": "Perttool.CheckResult.v2",
|
|
473
|
+
"Perttool.ProjectResult.v1": "Perttool.ProjectResult.v2",
|
|
474
|
+
"Perttool.AnalysisResult.v2": "Perttool.AnalysisResult.v3",
|
|
475
|
+
"Perttool.NextResult.v3": "Perttool.NextResult.v4",
|
|
476
|
+
});
|
|
477
|
+
function projectContract4Descriptor(descriptor) {
|
|
478
|
+
return Object.freeze({
|
|
479
|
+
...descriptor,
|
|
480
|
+
contractVersion: 4,
|
|
481
|
+
options: contract4Options(descriptor),
|
|
482
|
+
resultSchemas: Object.freeze(descriptor.resultSchemas.map((schema) => contract4SchemaReplacements[schema] ?? schema)),
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
function unitMigrationDescriptor() {
|
|
486
|
+
return Object.freeze({
|
|
487
|
+
contractVersion: 4,
|
|
488
|
+
path: Object.freeze(["project", "migrate-unit"]),
|
|
489
|
+
operation: "project.migrate-unit",
|
|
490
|
+
summary: "Previews exact whole-document Point and time-unit migration.",
|
|
491
|
+
operands: Object.freeze([
|
|
492
|
+
targetOperand("file", 0, "path-or-stdin"),
|
|
493
|
+
]),
|
|
494
|
+
options: Object.freeze([
|
|
495
|
+
targetValueOption("to-unit", {
|
|
496
|
+
valueType: "duration-unit",
|
|
497
|
+
required: true,
|
|
498
|
+
enumValues: ["day", "hour", "point"],
|
|
499
|
+
}),
|
|
500
|
+
targetValueOption("replacement-velocity", {
|
|
501
|
+
valueType: "velocity",
|
|
502
|
+
}),
|
|
503
|
+
...mutationTemplate.options,
|
|
504
|
+
]),
|
|
505
|
+
input: "document",
|
|
506
|
+
output: mutationTemplate.output,
|
|
507
|
+
stdin: mutationTemplate.stdin,
|
|
508
|
+
effect: "preview",
|
|
509
|
+
resultSchemas: Object.freeze([
|
|
510
|
+
"Perttool.UnitMigrationResult.v2",
|
|
511
|
+
"Perttool.CliError.v1",
|
|
512
|
+
]),
|
|
513
|
+
exitStatuses: mutationTemplate.exitStatuses,
|
|
514
|
+
examples: Object.freeze([
|
|
515
|
+
Object.freeze({
|
|
516
|
+
id: "preview",
|
|
517
|
+
invocation: "perttool project migrate-unit plan.pert --to-unit day --diff",
|
|
518
|
+
summary: "Preview exact Point-to-day migration.",
|
|
519
|
+
}),
|
|
520
|
+
Object.freeze({
|
|
521
|
+
id: "replacement",
|
|
522
|
+
invocation: "perttool project migrate-unit plan.pert --to-unit point --replacement-velocity 8p/4h --diff",
|
|
523
|
+
summary: "Preview time-to-Point migration with an explicit relationship.",
|
|
524
|
+
}),
|
|
525
|
+
]),
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
const contract4Descriptors = Object.freeze([
|
|
529
|
+
...contract3Descriptors.map(projectContract4Descriptor),
|
|
530
|
+
unitMigrationDescriptor(),
|
|
531
|
+
]);
|
|
532
|
+
const projectedByPath = new Map(contract4Descriptors.map((descriptor) => [pathKey(descriptor.path), descriptor]));
|
|
533
|
+
const orderedDescriptors = [helpDescriptor];
|
|
534
|
+
const guideDescriptor = projectedByPath.get("guide");
|
|
535
|
+
if (guideDescriptor === undefined) {
|
|
536
|
+
throw new Error("Contract 4 guide descriptor is missing");
|
|
537
|
+
}
|
|
538
|
+
orderedDescriptors.push(guideDescriptor);
|
|
539
|
+
for (const resource of resourceDefinitions) {
|
|
540
|
+
const commands = contract4Descriptors
|
|
541
|
+
.filter((descriptor) => descriptor.path.length === 2 && descriptor.path[0] === resource.name)
|
|
542
|
+
.sort((left, right) => resource.actionOrder.indexOf(left.path[1])
|
|
543
|
+
- resource.actionOrder.indexOf(right.path[1]));
|
|
544
|
+
if (commands.length === 0) {
|
|
545
|
+
throw new Error(`Contract 4 resource ${resource.name} has no commands`);
|
|
546
|
+
}
|
|
547
|
+
if (commands.length !== resource.actionOrder.length
|
|
548
|
+
|| commands.some((descriptor, index) => descriptor.path[1] !== resource.actionOrder[index])) {
|
|
549
|
+
throw new Error(`Contract 4 resource ${resource.name} action order is incomplete`);
|
|
550
|
+
}
|
|
551
|
+
orderedDescriptors.push(...commands);
|
|
552
|
+
}
|
|
553
|
+
if (new Set(orderedDescriptors.map((descriptor) => pathKey(descriptor.path))).size
|
|
554
|
+
!== orderedDescriptors.length) {
|
|
555
|
+
throw new Error("duplicate Contract 4 command path");
|
|
556
|
+
}
|
|
557
|
+
if (new Set(orderedDescriptors.map(({ operation }) => operation)).size
|
|
558
|
+
!== orderedDescriptors.length) {
|
|
559
|
+
throw new Error("duplicate Contract 4 command operation");
|
|
560
|
+
}
|
|
561
|
+
if (orderedDescriptors.length !== contract4Descriptors.length + 1) {
|
|
562
|
+
throw new Error("Contract 4 command projection has an unregistered resource");
|
|
563
|
+
}
|
|
564
|
+
export const CONTRACT4_COMMAND_REGISTRY = Object.freeze(orderedDescriptors);
|
|
565
|
+
export const CONTRACT4_COMMAND_HELP_REGISTRY = CONTRACT4_COMMAND_REGISTRY;
|
|
566
|
+
export function commandRegistryToJson() {
|
|
567
|
+
return CONTRACT4_COMMAND_REGISTRY.map(commandDescriptorToJson);
|
|
568
|
+
}
|
|
569
|
+
const contract4CommandsByPath = new Map(CONTRACT4_COMMAND_HELP_REGISTRY.map((descriptor) => [pathKey(descriptor.path), descriptor]));
|
|
570
|
+
const resourceSummaries = Object.freeze(resourceDefinitions.map((resource) => Object.freeze({
|
|
571
|
+
name: resource.name,
|
|
572
|
+
summary: resource.summary,
|
|
573
|
+
actions: resource.actionOrder,
|
|
574
|
+
})));
|
|
575
|
+
function diagnostic(code, message, query) {
|
|
576
|
+
return Object.freeze({
|
|
577
|
+
code,
|
|
578
|
+
severity: "error",
|
|
579
|
+
message,
|
|
580
|
+
data: Object.freeze({
|
|
581
|
+
resource: query.resource,
|
|
582
|
+
action: query.action,
|
|
583
|
+
}),
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
function result(query, resources, commands, diagnostics) {
|
|
587
|
+
return Object.freeze({
|
|
588
|
+
schemaVersion: "Perttool.CommandHelpResult.v1",
|
|
589
|
+
cliContractVersion: 4,
|
|
590
|
+
toolVersion: TOOL_VERSION,
|
|
591
|
+
operation: "help",
|
|
592
|
+
ok: diagnostics.length === 0,
|
|
593
|
+
query: Object.freeze({ ...query }),
|
|
594
|
+
resources: Object.freeze([...resources]),
|
|
595
|
+
commands: Object.freeze([...commands]),
|
|
596
|
+
diagnostics: Object.freeze([...diagnostics]),
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
export function getCommandDiscovery(query) {
|
|
600
|
+
if (query.resource === null) {
|
|
601
|
+
if (query.action !== null) {
|
|
602
|
+
return result(query, [], [], [diagnostic("PTHLP-002", "a command action requires a resource", query)]);
|
|
603
|
+
}
|
|
604
|
+
return result(query, resourceSummaries, CONTRACT4_COMMAND_HELP_REGISTRY, []);
|
|
605
|
+
}
|
|
606
|
+
if (query.action === null) {
|
|
607
|
+
const topLevel = contract4CommandsByPath.get(query.resource);
|
|
608
|
+
if (topLevel !== undefined && topLevel.path.length === 1) {
|
|
609
|
+
return result(query, [], [topLevel], []);
|
|
610
|
+
}
|
|
611
|
+
const resourceIndex = resourceDefinitions.findIndex(({ name }) => name === query.resource);
|
|
612
|
+
if (resourceIndex === -1) {
|
|
613
|
+
return result(query, [], [], [
|
|
614
|
+
diagnostic("PTHLP-002", `unknown command resource or top-level command: ${query.resource}`, query),
|
|
615
|
+
]);
|
|
616
|
+
}
|
|
617
|
+
const resource = resourceSummaries[resourceIndex];
|
|
618
|
+
if (resource === undefined) {
|
|
619
|
+
throw new Error("command resource summary invariant failed");
|
|
620
|
+
}
|
|
621
|
+
return result(query, [resource], CONTRACT4_COMMAND_HELP_REGISTRY.filter((descriptor) => descriptor.path.length === 2
|
|
622
|
+
&& descriptor.path[0] === query.resource), []);
|
|
623
|
+
}
|
|
624
|
+
const resource = resourceSummaries.find(({ name }) => name === query.resource);
|
|
625
|
+
if (resource === undefined) {
|
|
626
|
+
return result(query, [], [], [
|
|
627
|
+
diagnostic("PTHLP-002", `unknown command resource: ${query.resource}`, query),
|
|
628
|
+
]);
|
|
629
|
+
}
|
|
630
|
+
const command = contract4CommandsByPath.get(`${query.resource}\0${query.action}`);
|
|
631
|
+
if (command === undefined) {
|
|
632
|
+
return result(query, [], [], [
|
|
633
|
+
diagnostic("PTHLP-003", `unknown action ${query.action} for command resource ${query.resource}`, query),
|
|
634
|
+
]);
|
|
635
|
+
}
|
|
636
|
+
return result(query, [resource], [command], []);
|
|
637
|
+
}
|
|
638
|
+
function diagnosticToJson(value) {
|
|
639
|
+
return {
|
|
640
|
+
code: value.code,
|
|
641
|
+
severity: value.severity,
|
|
642
|
+
message: value.message,
|
|
643
|
+
data: value.data ?? null,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
export function commandHelpResultToJson(value) {
|
|
647
|
+
return {
|
|
648
|
+
schema_version: value.schemaVersion,
|
|
649
|
+
cli_contract_version: value.cliContractVersion,
|
|
650
|
+
tool_version: value.toolVersion,
|
|
651
|
+
operation: value.operation,
|
|
652
|
+
ok: value.ok,
|
|
653
|
+
query: {
|
|
654
|
+
resource: value.query.resource,
|
|
655
|
+
action: value.query.action,
|
|
656
|
+
},
|
|
657
|
+
resources: value.resources.map((resource) => ({
|
|
658
|
+
name: resource.name,
|
|
659
|
+
summary: resource.summary,
|
|
660
|
+
actions: resource.actions,
|
|
661
|
+
})),
|
|
662
|
+
commands: value.commands.map(commandDescriptorToJson),
|
|
663
|
+
diagnostics: value.diagnostics.map(diagnosticToJson),
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
export function serializeCommandHelpResult(value) {
|
|
667
|
+
return `${JSON.stringify(commandHelpResultToJson(value), null, 2)}\n`;
|
|
668
|
+
}
|
|
669
|
+
function list(values) {
|
|
670
|
+
return values.length === 0 ? "-" : values.join(", ");
|
|
671
|
+
}
|
|
672
|
+
function renderDescriptor(descriptor) {
|
|
673
|
+
const lines = [
|
|
674
|
+
`Command: perttool ${descriptor.path.join(" ")}`,
|
|
675
|
+
`Summary: ${descriptor.summary}`,
|
|
676
|
+
`Operation: ${descriptor.operation}`,
|
|
677
|
+
`CLI contract: ${descriptor.contractVersion}`,
|
|
678
|
+
"Operands:",
|
|
679
|
+
];
|
|
680
|
+
if (descriptor.operands.length === 0) {
|
|
681
|
+
lines.push(" -");
|
|
682
|
+
}
|
|
683
|
+
for (const operand of descriptor.operands) {
|
|
684
|
+
lines.push(` ${operand.position}: ${operand.name} type=${operand.valueType} required=${operand.required}`);
|
|
685
|
+
}
|
|
686
|
+
lines.push("Options:");
|
|
687
|
+
if (descriptor.options.length === 0) {
|
|
688
|
+
lines.push(" -");
|
|
689
|
+
}
|
|
690
|
+
for (const option of descriptor.options) {
|
|
691
|
+
lines.push(` --${option.name} kind=${option.kind} type=${option.valueType ?? "-"} required=${option.required} repeatable=${option.repeatable}`, ` default=${JSON.stringify(option.defaultValue)} enum=${list(option.enumValues)} shared=${option.sharedGroup ?? "-"}`, ` conflicts=${list(option.conflicts)} requires=${list(option.requires)}`, ` spelling cli=${option.spelling.cli} dsl=${option.spelling.dsl ?? "-"} json=${option.spelling.json}`);
|
|
692
|
+
}
|
|
693
|
+
lines.push(`Input: ${descriptor.input}`, `Stdin: document=${descriptor.stdin.document} artifact=${descriptor.stdin.artifact} request=${descriptor.stdin.request} mutually-exclusive=${descriptor.stdin.mutuallyExclusive}`, `Effect: ${descriptor.effect}`, `Output: formats=${descriptor.output.formats.join(",")} payload=${descriptor.output.payload} file-effect=${descriptor.output.fileEffect}`, `Result schemas: ${list(descriptor.resultSchemas)}`, "Exit statuses:");
|
|
694
|
+
for (const status of descriptor.exitStatuses) {
|
|
695
|
+
lines.push(` ${status.code}: ${status.meaning}`);
|
|
696
|
+
}
|
|
697
|
+
lines.push("Examples:");
|
|
698
|
+
for (const example of descriptor.examples) {
|
|
699
|
+
lines.push(` ${example.id}: ${example.invocation}`, ` ${example.summary}`);
|
|
700
|
+
}
|
|
701
|
+
return lines;
|
|
702
|
+
}
|
|
703
|
+
export function renderCommandHelpResult(value) {
|
|
704
|
+
if (!value.ok) {
|
|
705
|
+
return `${[
|
|
706
|
+
"Command help lookup failed.",
|
|
707
|
+
...value.diagnostics.map(({ code, severity, message }) => `${code} ${severity}: ${message}`),
|
|
708
|
+
].join("\n")}\n`;
|
|
709
|
+
}
|
|
710
|
+
if (value.query.resource === null) {
|
|
711
|
+
const topLevel = value.commands.filter(({ path }) => path.length === 1);
|
|
712
|
+
const lines = [
|
|
713
|
+
"perttool command catalog (CLI Contract 4)",
|
|
714
|
+
"",
|
|
715
|
+
"Top-level commands:",
|
|
716
|
+
...topLevel.map((descriptor) => ` ${descriptor.path[0]} ${descriptor.summary}`),
|
|
717
|
+
"",
|
|
718
|
+
"Resources:",
|
|
719
|
+
];
|
|
720
|
+
for (const resource of value.resources) {
|
|
721
|
+
lines.push(` ${resource.name} ${resource.summary}`, ...resource.actions.map((action) => {
|
|
722
|
+
const command = value.commands.find(({ path }) => path.length === 2
|
|
723
|
+
&& path[0] === resource.name
|
|
724
|
+
&& path[1] === action);
|
|
725
|
+
if (command === undefined) {
|
|
726
|
+
throw new Error("command resource projection invariant failed");
|
|
727
|
+
}
|
|
728
|
+
return ` ${action} ${command.summary}`;
|
|
729
|
+
}));
|
|
730
|
+
}
|
|
731
|
+
return `${lines.join("\n")}\n`;
|
|
732
|
+
}
|
|
733
|
+
if (value.query.action === null && value.resources.length === 1) {
|
|
734
|
+
const resource = value.resources[0];
|
|
735
|
+
if (resource === undefined) {
|
|
736
|
+
throw new Error("command resource projection invariant failed");
|
|
737
|
+
}
|
|
738
|
+
return `${[
|
|
739
|
+
`perttool ${resource.name} commands`,
|
|
740
|
+
resource.summary,
|
|
741
|
+
"",
|
|
742
|
+
"Actions:",
|
|
743
|
+
...value.commands.map((descriptor) => ` ${descriptor.path[1]} ${descriptor.summary}`),
|
|
744
|
+
].join("\n")}\n`;
|
|
745
|
+
}
|
|
746
|
+
const descriptor = value.commands[0];
|
|
747
|
+
if (descriptor === undefined) {
|
|
748
|
+
throw new Error("command help result has no descriptor");
|
|
749
|
+
}
|
|
750
|
+
return `${renderDescriptor(descriptor).join("\n")}\n`;
|
|
751
|
+
}
|
|
752
|
+
//# sourceMappingURL=discovery.js.map
|