perttool 0.1.0 → 0.2.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 +66 -33
- package/README.md +172 -110
- package/dist/application/analyze.js +5 -5
- package/dist/application/analyze.js.map +1 -1
- package/dist/application/init.d.ts +42 -0
- package/dist/application/init.d.ts.map +1 -0
- package/dist/application/init.js +217 -0
- package/dist/application/init.js.map +1 -0
- package/dist/application/mutate.d.ts.map +1 -1
- package/dist/application/mutate.js +13 -9
- package/dist/application/mutate.js.map +1 -1
- package/dist/cli.js +245 -372
- 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 +625 -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 +951 -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/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 +69 -80
- package/dist/help/registry.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/io/safe-write.js +17 -17
- package/dist/io/safe-write.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.js +26 -26
- package/dist/mutation/milestone.js.map +1 -1
- package/dist/mutation/project.js +14 -14
- 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/task.js +40 -40
- package/dist/mutation/task.js.map +1 -1
- package/dist/mutation/types.d.ts +26 -1
- package/dist/mutation/types.d.ts.map +1 -1
- package/dist/parser/document-parser.js +17 -17
- package/dist/parser/document-parser.js.map +1 -1
- package/dist/semantic/validator.js +41 -41
- 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
package/dist/cli.js
CHANGED
|
@@ -5,14 +5,18 @@ import { TextDecoder } from "node:util";
|
|
|
5
5
|
import { analyzeDocument } from "./application/analyze.js";
|
|
6
6
|
import { checkDocument } from "./application/check.js";
|
|
7
7
|
import { planFormat } from "./application/format.js";
|
|
8
|
+
import { planProjectInit, projectInitResultToJson, renderProjectInitResult, withProjectInitOutput, } from "./application/init.js";
|
|
8
9
|
import { planBatchMutation, planMutation } from "./application/mutate.js";
|
|
9
10
|
import { selectNextTasks } from "./application/next.js";
|
|
10
11
|
import { getProjectMetadata, } from "./application/project.js";
|
|
11
12
|
import { getAgentHelp } from "./application/agent-help.js";
|
|
12
13
|
import { exportMermaid, } from "./conversion/mermaid.js";
|
|
13
14
|
import { importMermaid } from "./conversion/mermaid-import.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
15
|
+
import { getGuide, renderGuideResult, serializeGuideResult, } from "./help/guide.js";
|
|
16
|
+
import { commandOptionSets, } from "./command/registry.js";
|
|
17
|
+
import { CONTRACT3_COMMAND_REGISTRY, getCommandDiscovery, renderCommandHelpResult, serializeCommandHelpResult, } from "./command/discovery.js";
|
|
18
|
+
import { handlerCommandUsageError, renderCommandUsageError, serializeCommandUsageError, validateCommandInvocation, } from "./command/usage.js";
|
|
19
|
+
import { agentGuidanceResultToJson } from "./guidance/projection.js";
|
|
16
20
|
import { agentGuidanceExitCode, renderAgentGuidanceText, } from "./guidance/text.js";
|
|
17
21
|
import { documentContentFromBytes, readDocumentFile, } from "./io/document-file.js";
|
|
18
22
|
import { createArtifactFile, createDocumentFile, replaceDocumentFile, SafeWriteConflictError, SafeWriteVerificationError, } from "./io/safe-write.js";
|
|
@@ -25,183 +29,6 @@ import { TOOL_VERSION } from "./version.js";
|
|
|
25
29
|
class UsageError extends Error {
|
|
26
30
|
code = "PTCLI-001";
|
|
27
31
|
}
|
|
28
|
-
function topLevelHelp() {
|
|
29
|
-
return [
|
|
30
|
-
"perttool - document-based PERT/CPM task management",
|
|
31
|
-
"",
|
|
32
|
-
"Usage:",
|
|
33
|
-
" perttool --version",
|
|
34
|
-
" perttool --help",
|
|
35
|
-
" perttool dsl check <file> [--format text|json]",
|
|
36
|
-
" perttool dsl format <file> [--check] [--diff] [--format text|json]",
|
|
37
|
-
" perttool dsl help [topic [subtopic]] [--level index|quick|detail] [--format text|json]",
|
|
38
|
-
" perttool agent help [provider [surface]] [--level index|quick|detail] [--format text|json]",
|
|
39
|
-
" perttool project show <file> [--format text|json]",
|
|
40
|
-
" perttool project set <file> [field options] [--diff] [--write | --out <path>]",
|
|
41
|
-
" perttool dag analyze <file> [--schedule precedence|resource|both] [--format text|json]",
|
|
42
|
-
" perttool dag next <file> [--capacity <resource-id>=<integer>] [--format text|json]",
|
|
43
|
-
" perttool dag advance <file> [--diff] [--write | --out <path>] [--format text|json]",
|
|
44
|
-
" perttool dag render <file> --to mermaid [--profile perttool|plain] [--format text|json]",
|
|
45
|
-
" perttool dag import <file> --from mermaid [--strict-loss] [--out <path>] [--format text|json]",
|
|
46
|
-
" perttool task add|set|remove|finish ...",
|
|
47
|
-
" perttool milestone add|set|remove ...",
|
|
48
|
-
" perttool resource add|set|remove ...",
|
|
49
|
-
" perttool mutation apply <file> --request <json-file|-> [--diff] [--format text|json]",
|
|
50
|
-
"",
|
|
51
|
-
"Format and mutation commands preview by default; use --write or --out for explicit writes.",
|
|
52
|
-
].join("\n");
|
|
53
|
-
}
|
|
54
|
-
function commandHelp(resource, action) {
|
|
55
|
-
if (resource === "agent" && action === "help") {
|
|
56
|
-
return getAgentHelpCommandHelp().syntax.join("\n");
|
|
57
|
-
}
|
|
58
|
-
if (resource === "dsl" && action === "check") {
|
|
59
|
-
return [
|
|
60
|
-
"Usage: perttool dsl check <file>",
|
|
61
|
-
" [--warnings-as-errors]",
|
|
62
|
-
" [--max-diagnostics <integer>]",
|
|
63
|
-
" [--format text|json]",
|
|
64
|
-
" [--color auto|always|never]",
|
|
65
|
-
].join("\n");
|
|
66
|
-
}
|
|
67
|
-
if (resource === "dsl" && action === "format")
|
|
68
|
-
return [
|
|
69
|
-
"Usage: perttool dsl format <file>",
|
|
70
|
-
" [--check] [--diff]",
|
|
71
|
-
" [--write [--expect-digest <digest>] | --out <path>]",
|
|
72
|
-
" [--max-diagnostics <integer>] [--warnings-as-errors]",
|
|
73
|
-
" [--format text|json] [--color auto|always|never]",
|
|
74
|
-
].join("\n");
|
|
75
|
-
if (resource === "dsl" && action === "help")
|
|
76
|
-
return [
|
|
77
|
-
"Usage: perttool dsl help [topic [subtopic]]",
|
|
78
|
-
" [--level index|quick|detail]",
|
|
79
|
-
" [--format text|json]",
|
|
80
|
-
" [--color auto|always|never]",
|
|
81
|
-
].join("\n");
|
|
82
|
-
if (resource === "project" && action === "show")
|
|
83
|
-
return [
|
|
84
|
-
"Usage: perttool project show <file>",
|
|
85
|
-
" [--max-diagnostics <integer>] [--warnings-as-errors]",
|
|
86
|
-
" [--format text|json] [--color auto|always|never]",
|
|
87
|
-
].join("\n");
|
|
88
|
-
if (resource === "project" && action === "set")
|
|
89
|
-
return [
|
|
90
|
-
"Usage: perttool project set <file> [field options]",
|
|
91
|
-
" [--id <id>] [--version <integer>] [--title <text>] [--description <text>]",
|
|
92
|
-
" [--as-of <date-or-date-time>] [--duration-unit day|hour|point]",
|
|
93
|
-
" [--velocity <velocity>] [--finish <milestone-id>]",
|
|
94
|
-
" [--critical-epsilon <duration>] [--target-duration <duration>]",
|
|
95
|
-
" [--clear description|as_of|velocity|critical_epsilon|target_duration]...",
|
|
96
|
-
" [--diff] [--write [--expect-digest <digest>] | --out <path>]",
|
|
97
|
-
" [--max-diagnostics <integer>] [--warnings-as-errors]",
|
|
98
|
-
" [--format text|json] [--color auto|always|never]",
|
|
99
|
-
].join("\n");
|
|
100
|
-
if (resource === "dag" && action === "analyze")
|
|
101
|
-
return [
|
|
102
|
-
"Usage: perttool dag analyze <file>",
|
|
103
|
-
" [--schedule precedence|resource|both]",
|
|
104
|
-
" [--capacity <resource-id>=<integer>]...",
|
|
105
|
-
" [--max-paths <integer>] [--precision <integer>]",
|
|
106
|
-
" [--max-diagnostics <integer>]",
|
|
107
|
-
" [--warnings-as-errors]",
|
|
108
|
-
" [--format text|json] [--color auto|always|never]",
|
|
109
|
-
].join("\n");
|
|
110
|
-
if (resource === "dag" && action === "next")
|
|
111
|
-
return [
|
|
112
|
-
"Usage: perttool dag next <file>",
|
|
113
|
-
" [--capacity <resource-id>=<integer>]...",
|
|
114
|
-
" [--explain-depth <integer>] [--precision <integer>]",
|
|
115
|
-
" [--max-diagnostics <integer>]",
|
|
116
|
-
" [--warnings-as-errors]",
|
|
117
|
-
" [--format text|json] [--color auto|always|never]",
|
|
118
|
-
"Output: Perttool.NextResult.v3 with a complete recommendation graph in JSON.",
|
|
119
|
-
"Consumers must inspect schema_version before using recommendation authority.",
|
|
120
|
-
].join("\n");
|
|
121
|
-
if (resource === "dag" && action === "advance")
|
|
122
|
-
return [
|
|
123
|
-
"Usage: perttool dag advance <file>",
|
|
124
|
-
" [--diff] [--write [--expect-digest <digest>] | --out <path>]",
|
|
125
|
-
" [--max-diagnostics <integer>] [--warnings-as-errors]",
|
|
126
|
-
" [--format text|json] [--color auto|always|never]",
|
|
127
|
-
].join("\n");
|
|
128
|
-
if (resource === "dag" && action === "render")
|
|
129
|
-
return [
|
|
130
|
-
"Usage: perttool dag render <file> --to mermaid",
|
|
131
|
-
" [--profile perttool|plain] [--analysis none|precedence|resource|both]",
|
|
132
|
-
" [--capacity <resource-id>=<integer>]... [--strict-loss] [--out <path>]",
|
|
133
|
-
" [--max-diagnostics <integer>] [--warnings-as-errors]",
|
|
134
|
-
" [--format text|json] [--color auto|always|never]",
|
|
135
|
-
].join("\n");
|
|
136
|
-
if (resource === "dag" && action === "import")
|
|
137
|
-
return [
|
|
138
|
-
"Usage: perttool dag import <file> --from mermaid",
|
|
139
|
-
" [--strict-loss] [--out <path>]",
|
|
140
|
-
" [--max-diagnostics <integer>] [--warnings-as-errors]",
|
|
141
|
-
" [--format text|json] [--color auto|always|never]",
|
|
142
|
-
].join("\n");
|
|
143
|
-
const preview = " [--diff] [--write [--expect-digest <digest>] | --out <path>] [--max-diagnostics <integer>] [--warnings-as-errors] [--format text|json] [--color auto|always|never]";
|
|
144
|
-
if (resource === "task" && action === "add")
|
|
145
|
-
return [
|
|
146
|
-
"Usage: perttool task add <file> <id> <from> <to>",
|
|
147
|
-
" --title <text> (--duration <duration> | --optimistic <duration> --most-likely <duration> --pessimistic <duration>)",
|
|
148
|
-
" [--description <text>] [--status planned|active|blocked|done] [--priority <integer>]",
|
|
149
|
-
" [--owner <text>] [--blocked-reason <text>] [--source <text>] [--tag <tag>]... [--require <resource-id>=<integer>]...",
|
|
150
|
-
preview,
|
|
151
|
-
].join("\n");
|
|
152
|
-
if (resource === "task" && action === "set")
|
|
153
|
-
return [
|
|
154
|
-
"Usage: perttool task set <file> <id> [field options]",
|
|
155
|
-
" [--from <id>] [--to <id>] [--title <text>] [--description <text>] [--duration <duration>]",
|
|
156
|
-
" [--optimistic <duration> --most-likely <duration> --pessimistic <duration>]",
|
|
157
|
-
" [--status planned|active|blocked|done] [--priority <integer>] [--owner <text>]",
|
|
158
|
-
" [--blocked-reason <text>] [--source <text>] [--require <resource-id>=<integer>]...",
|
|
159
|
-
" [--add-tag <tag>]... [--remove-tag <tag>]... [--remove-require <resource-id>]... [--clear <field>]...",
|
|
160
|
-
preview,
|
|
161
|
-
].join("\n");
|
|
162
|
-
if (resource === "task")
|
|
163
|
-
return [
|
|
164
|
-
`Usage: perttool task ${action} <file> <id>`,
|
|
165
|
-
preview,
|
|
166
|
-
].join("\n");
|
|
167
|
-
if (resource === "milestone" && action === "add")
|
|
168
|
-
return [
|
|
169
|
-
"Usage: perttool milestone add <file> <id> --title <text>",
|
|
170
|
-
" [--description <text>] [--state planned|reached] [--tag <tag>]...",
|
|
171
|
-
preview,
|
|
172
|
-
].join("\n");
|
|
173
|
-
if (resource === "milestone" && action === "set")
|
|
174
|
-
return [
|
|
175
|
-
"Usage: perttool milestone set <file> <id> [--title <text>] [--description <text>] [--state planned|reached]",
|
|
176
|
-
" [--add-tag <tag>]... [--remove-tag <tag>]... [--clear description|state|tags]...",
|
|
177
|
-
preview,
|
|
178
|
-
].join("\n");
|
|
179
|
-
if (resource === "milestone")
|
|
180
|
-
return [
|
|
181
|
-
`Usage: perttool milestone ${action} <file> <id>`,
|
|
182
|
-
preview,
|
|
183
|
-
].join("\n");
|
|
184
|
-
if (resource === "resource" && action === "add")
|
|
185
|
-
return [
|
|
186
|
-
"Usage: perttool resource add <file> <id> --title <text> --capacity <integer>",
|
|
187
|
-
" [--description <text>]",
|
|
188
|
-
preview,
|
|
189
|
-
].join("\n");
|
|
190
|
-
if (resource === "resource" && action === "set")
|
|
191
|
-
return [
|
|
192
|
-
"Usage: perttool resource set <file> <id> [--title <text>] [--description <text>] [--capacity <integer>] [--clear description]",
|
|
193
|
-
preview,
|
|
194
|
-
].join("\n");
|
|
195
|
-
if (resource === "resource")
|
|
196
|
-
return [
|
|
197
|
-
`Usage: perttool resource ${action} <file> <id>`,
|
|
198
|
-
preview,
|
|
199
|
-
].join("\n");
|
|
200
|
-
return [
|
|
201
|
-
"Usage: perttool mutation apply <file> --request <json-file|->",
|
|
202
|
-
preview,
|
|
203
|
-
].join("\n");
|
|
204
|
-
}
|
|
205
32
|
function parseOptions(args, valueOptions, flagOptions, repeatableOptions = new Set()) {
|
|
206
33
|
const positionals = [];
|
|
207
34
|
const values = new Map();
|
|
@@ -251,6 +78,14 @@ function parseOptions(args, valueOptions, flagOptions, repeatableOptions = new S
|
|
|
251
78
|
}
|
|
252
79
|
return { positionals, values, repeatedValues, flags };
|
|
253
80
|
}
|
|
81
|
+
function parseCommandOptions(operation, args) {
|
|
82
|
+
const descriptor = CONTRACT3_COMMAND_REGISTRY.find((candidate) => candidate.operation === operation);
|
|
83
|
+
if (descriptor === undefined) {
|
|
84
|
+
throw new Error(`command descriptor is missing for ${operation}`);
|
|
85
|
+
}
|
|
86
|
+
const optionSets = commandOptionSets(descriptor);
|
|
87
|
+
return parseOptions(args, optionSets.values, optionSets.flags, optionSets.repeatable);
|
|
88
|
+
}
|
|
254
89
|
function outputFormat(value) {
|
|
255
90
|
if (value === undefined || value === "text")
|
|
256
91
|
return "text";
|
|
@@ -300,7 +135,8 @@ function jsonDiagnostic(diagnostic) {
|
|
|
300
135
|
message: related.message,
|
|
301
136
|
span: jsonSpan(related.span),
|
|
302
137
|
})),
|
|
303
|
-
help_topic:
|
|
138
|
+
help_topic: null,
|
|
139
|
+
guide_topic: diagnostic.helpTopic ?? null,
|
|
304
140
|
expected_syntax: diagnostic.expectedSyntax ?? null,
|
|
305
141
|
fixes: [],
|
|
306
142
|
data: diagnostic.data ?? {},
|
|
@@ -326,7 +162,7 @@ function renderDiagnostic(diagnostic, source, mode) {
|
|
|
326
162
|
}
|
|
327
163
|
if (diagnostic.helpTopic !== undefined) {
|
|
328
164
|
const [topic, subtopic] = diagnostic.helpTopic.split(".", 2);
|
|
329
|
-
lines.push(`
|
|
165
|
+
lines.push(` guide: perttool guide ${topic}${subtopic === undefined ? "" : ` ${subtopic}`} --level quick`);
|
|
330
166
|
}
|
|
331
167
|
return lines.join("\n");
|
|
332
168
|
}
|
|
@@ -369,6 +205,7 @@ function cliError(error, exitCode, operation, json) {
|
|
|
369
205
|
if (json) {
|
|
370
206
|
writeJson({
|
|
371
207
|
schema_version: "Perttool.CliError.v1",
|
|
208
|
+
cli_contract_version: 3,
|
|
372
209
|
tool_version: TOOL_VERSION,
|
|
373
210
|
operation,
|
|
374
211
|
ok: false,
|
|
@@ -381,9 +218,9 @@ function cliError(error, exitCode, operation, json) {
|
|
|
381
218
|
return exitCode;
|
|
382
219
|
}
|
|
383
220
|
async function runCheck(args) {
|
|
384
|
-
const parsed =
|
|
221
|
+
const parsed = parseCommandOptions("document.check", args);
|
|
385
222
|
if (parsed.positionals.length !== 1) {
|
|
386
|
-
throw new UsageError("
|
|
223
|
+
throw new UsageError("document check requires exactly one <file>");
|
|
387
224
|
}
|
|
388
225
|
const format = outputFormat(parsed.values.get("format"));
|
|
389
226
|
const color = colorMode(parsed.values.get("color"), format);
|
|
@@ -395,7 +232,7 @@ async function runCheck(args) {
|
|
|
395
232
|
input = await readDocument(sourceOperand);
|
|
396
233
|
}
|
|
397
234
|
catch (error) {
|
|
398
|
-
return cliError(error instanceof Error ? error : new Error(String(error)), 3, "
|
|
235
|
+
return cliError(error instanceof Error ? error : new Error(String(error)), 3, "document.check", format === "json");
|
|
399
236
|
}
|
|
400
237
|
const result = checkDocument(input.text, { maxDiagnostics });
|
|
401
238
|
const warningsAsErrors = parsed.flags.has("warnings-as-errors");
|
|
@@ -404,8 +241,9 @@ async function runCheck(args) {
|
|
|
404
241
|
if (format === "json") {
|
|
405
242
|
writeJson({
|
|
406
243
|
schema_version: "Perttool.CheckResult.v1",
|
|
244
|
+
cli_contract_version: 3,
|
|
407
245
|
tool_version: TOOL_VERSION,
|
|
408
|
-
operation: "
|
|
246
|
+
operation: "document.check",
|
|
409
247
|
ok,
|
|
410
248
|
document_id: result.documentId,
|
|
411
249
|
source,
|
|
@@ -460,7 +298,7 @@ function renderProjectText(project) {
|
|
|
460
298
|
].join("\n");
|
|
461
299
|
}
|
|
462
300
|
async function runProjectShow(args) {
|
|
463
|
-
const parsed =
|
|
301
|
+
const parsed = parseCommandOptions("project.show", args);
|
|
464
302
|
if (parsed.positionals.length !== 1) {
|
|
465
303
|
throw new UsageError("project show requires exactly one <file>");
|
|
466
304
|
}
|
|
@@ -484,6 +322,7 @@ async function runProjectShow(args) {
|
|
|
484
322
|
if (format === "json") {
|
|
485
323
|
writeJson({
|
|
486
324
|
schema_version: "Perttool.ProjectResult.v1",
|
|
325
|
+
cli_contract_version: 3,
|
|
487
326
|
tool_version: TOOL_VERSION,
|
|
488
327
|
operation: "project.show",
|
|
489
328
|
ok,
|
|
@@ -509,6 +348,56 @@ async function runProjectShow(args) {
|
|
|
509
348
|
}
|
|
510
349
|
return ok ? 0 : 1;
|
|
511
350
|
}
|
|
351
|
+
async function runProjectInit(args) {
|
|
352
|
+
const parsed = parseCommandOptions("project.init", args);
|
|
353
|
+
const format = outputFormat(parsed.values.get("format"));
|
|
354
|
+
const color = colorMode(parsed.values.get("color"), format);
|
|
355
|
+
const durationUnit = enumOption(requiredOption(parsed, "duration-unit"), "duration-unit", new Set(["day", "hour", "point"]));
|
|
356
|
+
const version = optionalInteger(parsed, "version", 0, 2_147_483_647);
|
|
357
|
+
let result = planProjectInit({
|
|
358
|
+
projectId: parsed.positionals[0],
|
|
359
|
+
title: requiredOption(parsed, "title"),
|
|
360
|
+
durationUnit,
|
|
361
|
+
initialMilestone: requiredOption(parsed, "initial-milestone"),
|
|
362
|
+
initialMilestoneTitle: requiredOption(parsed, "initial-milestone-title"),
|
|
363
|
+
finish: requiredOption(parsed, "finish"),
|
|
364
|
+
...(version === undefined ? {} : { version }),
|
|
365
|
+
...(parsed.values.get("as-of") === undefined
|
|
366
|
+
? {}
|
|
367
|
+
: { asOf: parsed.values.get("as-of") }),
|
|
368
|
+
...(parsed.values.get("velocity") === undefined
|
|
369
|
+
? {}
|
|
370
|
+
: { velocity: parsed.values.get("velocity") }),
|
|
371
|
+
});
|
|
372
|
+
let writeResult = null;
|
|
373
|
+
const output = parsed.values.get("out");
|
|
374
|
+
if (result.ok && output !== undefined) {
|
|
375
|
+
try {
|
|
376
|
+
writeResult = await createDocumentFile(output, result.candidateText);
|
|
377
|
+
result = withProjectInitOutput(result, writeResult);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
return writeFailureExit(error, "project.init", format === "json");
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (format === "json") {
|
|
384
|
+
writeJson(projectInitResultToJson(result));
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
if (result.ok) {
|
|
388
|
+
if (writeResult === null) {
|
|
389
|
+
process.stdout.write(renderProjectInitResult(result));
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
process.stderr.write(renderWriteSummary("project.init", writeResult));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
for (const diagnostic of result.diagnostics) {
|
|
396
|
+
process.stderr.write(`${renderDiagnostic(diagnostic, "<project-init>", color)}\n`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return result.ok ? 0 : 1;
|
|
400
|
+
}
|
|
512
401
|
function boundedInteger(raw, option, defaultValue, minimum, maximum) {
|
|
513
402
|
if (raw === undefined)
|
|
514
403
|
return defaultValue;
|
|
@@ -546,14 +435,6 @@ function capacityOverrides(values) {
|
|
|
546
435
|
}
|
|
547
436
|
return overrides;
|
|
548
437
|
}
|
|
549
|
-
const mutationCommonValueOptions = [
|
|
550
|
-
"format",
|
|
551
|
-
"color",
|
|
552
|
-
"max-diagnostics",
|
|
553
|
-
"out",
|
|
554
|
-
"expect-digest",
|
|
555
|
-
];
|
|
556
|
-
const mutationCommonFlagOptions = ["diff", "write", "warnings-as-errors"];
|
|
557
438
|
function editingWriteRequest(parsed, sourceOperand, check = false) {
|
|
558
439
|
const write = parsed.flags.has("write");
|
|
559
440
|
const out = parsed.values.get("out");
|
|
@@ -594,12 +475,12 @@ function assertExpectedDigest(request, initialDigest) {
|
|
|
594
475
|
if (request.mode === "in_place" &&
|
|
595
476
|
request.expectedDigest !== undefined &&
|
|
596
477
|
request.expectedDigest !== initialDigest) {
|
|
597
|
-
throw new SafeWriteConflictError("expected_digest_mismatch", "--expect-digest
|
|
478
|
+
throw new SafeWriteConflictError("expected_digest_mismatch", "--expect-digest does not match the initial document digest");
|
|
598
479
|
}
|
|
599
480
|
}
|
|
600
481
|
async function commitCandidate(request, candidateText, initialDigest) {
|
|
601
482
|
if (candidateText === null) {
|
|
602
|
-
throw new SafeWriteVerificationError("invalid_candidate", "successful editing result
|
|
483
|
+
throw new SafeWriteVerificationError("invalid_candidate", "successful editing result has no candidate text");
|
|
603
484
|
}
|
|
604
485
|
return request.mode === "in_place"
|
|
605
486
|
? replaceDocumentFile(request.target, candidateText, {
|
|
@@ -623,9 +504,9 @@ function renderWriteSummary(operation, result) {
|
|
|
623
504
|
return `WRITE ${operation} mode=${result.mode} target=${result.target} digest=${result.digest} written=${result.written}\n`;
|
|
624
505
|
}
|
|
625
506
|
async function runFormat(args) {
|
|
626
|
-
const parsed =
|
|
507
|
+
const parsed = parseCommandOptions("document.format", args);
|
|
627
508
|
if (parsed.positionals.length !== 1) {
|
|
628
|
-
throw new UsageError("
|
|
509
|
+
throw new UsageError("document format requires exactly one <file>");
|
|
629
510
|
}
|
|
630
511
|
const format = outputFormat(parsed.values.get("format"));
|
|
631
512
|
const color = colorMode(parsed.values.get("color"), format);
|
|
@@ -638,7 +519,7 @@ async function runFormat(args) {
|
|
|
638
519
|
input = await readDocument(sourceOperand);
|
|
639
520
|
}
|
|
640
521
|
catch (error) {
|
|
641
|
-
return cliError(error instanceof Error ? error : new Error(String(error)), 3, "
|
|
522
|
+
return cliError(error instanceof Error ? error : new Error(String(error)), 3, "document.format", format === "json");
|
|
642
523
|
}
|
|
643
524
|
const result = planFormat(input.text, {
|
|
644
525
|
maxDiagnostics,
|
|
@@ -659,14 +540,15 @@ async function runFormat(args) {
|
|
|
659
540
|
}
|
|
660
541
|
}
|
|
661
542
|
catch (error) {
|
|
662
|
-
return writeFailureExit(error, "
|
|
543
|
+
return writeFailureExit(error, "document.format", format === "json");
|
|
663
544
|
}
|
|
664
545
|
}
|
|
665
546
|
if (format === "json") {
|
|
666
547
|
writeJson({
|
|
667
548
|
schema_version: "Perttool.FormatResult.v1",
|
|
549
|
+
cli_contract_version: 3,
|
|
668
550
|
tool_version: TOOL_VERSION,
|
|
669
|
-
operation: "
|
|
551
|
+
operation: "document.format",
|
|
670
552
|
ok,
|
|
671
553
|
document_id: result.documentId,
|
|
672
554
|
source,
|
|
@@ -680,7 +562,7 @@ async function runFormat(args) {
|
|
|
680
562
|
const candidateAllowed = result.ok && !warningFailure;
|
|
681
563
|
if (candidateAllowed) {
|
|
682
564
|
if (writeResult !== null) {
|
|
683
|
-
process.stderr.write(renderWriteSummary("
|
|
565
|
+
process.stderr.write(renderWriteSummary("document.format", writeResult));
|
|
684
566
|
}
|
|
685
567
|
else if (parsed.flags.has("check")) {
|
|
686
568
|
if (parsed.flags.has("diff"))
|
|
@@ -689,7 +571,7 @@ async function runFormat(args) {
|
|
|
689
571
|
else {
|
|
690
572
|
process.stdout.write(parsed.flags.has("diff") ? (result.diff ?? "") : (result.updatedText ?? ""));
|
|
691
573
|
if (!parsed.flags.has("diff")) {
|
|
692
|
-
process.stderr.write(`PREVIEW
|
|
574
|
+
process.stderr.write(`PREVIEW document.format changed=${result.changed} original_digest=${result.originalDigest} updated_digest=${result.updatedDigest}\n`);
|
|
693
575
|
}
|
|
694
576
|
}
|
|
695
577
|
}
|
|
@@ -702,50 +584,6 @@ async function runFormat(args) {
|
|
|
702
584
|
}
|
|
703
585
|
return ok ? 0 : 1;
|
|
704
586
|
}
|
|
705
|
-
function mutationOptionSets(resource, action) {
|
|
706
|
-
const values = new Set(mutationCommonValueOptions);
|
|
707
|
-
const flags = new Set(mutationCommonFlagOptions);
|
|
708
|
-
const repeatable = new Set();
|
|
709
|
-
const addValues = (...names) => {
|
|
710
|
-
for (const name of names)
|
|
711
|
-
values.add(name);
|
|
712
|
-
};
|
|
713
|
-
const addRepeatable = (...names) => {
|
|
714
|
-
for (const name of names)
|
|
715
|
-
repeatable.add(name);
|
|
716
|
-
};
|
|
717
|
-
if (resource === "project" && action === "set") {
|
|
718
|
-
addValues("id", "version", "title", "description", "as-of", "duration-unit", "velocity", "finish", "critical-epsilon", "target-duration");
|
|
719
|
-
addRepeatable("clear");
|
|
720
|
-
}
|
|
721
|
-
else if (resource === "task" && action === "add") {
|
|
722
|
-
addValues("title", "description", "duration", "optimistic", "most-likely", "pessimistic", "status", "priority", "owner", "blocked-reason", "source");
|
|
723
|
-
addRepeatable("tag", "require");
|
|
724
|
-
}
|
|
725
|
-
else if (resource === "task" && action === "set") {
|
|
726
|
-
addValues("from", "to", "title", "description", "duration", "optimistic", "most-likely", "pessimistic", "status", "priority", "owner", "blocked-reason", "source");
|
|
727
|
-
addRepeatable("require", "add-tag", "remove-tag", "remove-require", "clear");
|
|
728
|
-
}
|
|
729
|
-
else if (resource === "milestone" && action === "add") {
|
|
730
|
-
addValues("title", "description", "state");
|
|
731
|
-
addRepeatable("tag");
|
|
732
|
-
}
|
|
733
|
-
else if (resource === "milestone" && action === "set") {
|
|
734
|
-
addValues("title", "description", "state");
|
|
735
|
-
addRepeatable("add-tag", "remove-tag", "clear");
|
|
736
|
-
}
|
|
737
|
-
else if (resource === "resource" && action === "add") {
|
|
738
|
-
addValues("title", "description", "capacity");
|
|
739
|
-
}
|
|
740
|
-
else if (resource === "resource" && action === "set") {
|
|
741
|
-
addValues("title", "description", "capacity");
|
|
742
|
-
addRepeatable("clear");
|
|
743
|
-
}
|
|
744
|
-
else if (resource === "mutation" && action === "apply") {
|
|
745
|
-
addValues("request");
|
|
746
|
-
}
|
|
747
|
-
return { values, flags, repeatable };
|
|
748
|
-
}
|
|
749
587
|
function requiredOption(parsed, name) {
|
|
750
588
|
const value = parsed.values.get(name);
|
|
751
589
|
if (value === undefined)
|
|
@@ -1086,6 +924,37 @@ function resourceMutationFromOptions(action, parsed) {
|
|
|
1086
924
|
...(clear.length === 0 ? {} : { clear }),
|
|
1087
925
|
};
|
|
1088
926
|
}
|
|
927
|
+
function gateMutationFromOptions(action, parsed) {
|
|
928
|
+
const expectedPositionals = action === "add" ? 4 : 2;
|
|
929
|
+
if (parsed.positionals.length !== expectedPositionals) {
|
|
930
|
+
throw new UsageError(`gate ${action} requires ${action === "add" ? "<file> <id> <from> <to>" : "<file> <id>"}`);
|
|
931
|
+
}
|
|
932
|
+
const id = parsed.positionals[1];
|
|
933
|
+
if (action === "remove")
|
|
934
|
+
return { kind: "gate.remove", id };
|
|
935
|
+
if (action === "add") {
|
|
936
|
+
return {
|
|
937
|
+
kind: "gate.add",
|
|
938
|
+
id,
|
|
939
|
+
from: parsed.positionals[2],
|
|
940
|
+
to: parsed.positionals[3],
|
|
941
|
+
gate: { reason: requiredOption(parsed, "reason") },
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
const from = parsed.values.get("from");
|
|
945
|
+
const to = parsed.values.get("to");
|
|
946
|
+
const reason = parsed.values.get("reason");
|
|
947
|
+
if (from === undefined && to === undefined && reason === undefined) {
|
|
948
|
+
throw new UsageError("gate set requires --from, --to, or --reason");
|
|
949
|
+
}
|
|
950
|
+
return {
|
|
951
|
+
kind: "gate.set",
|
|
952
|
+
id,
|
|
953
|
+
...(from === undefined ? {} : { from }),
|
|
954
|
+
...(to === undefined ? {} : { to }),
|
|
955
|
+
...(reason === undefined ? {} : { set: { reason } }),
|
|
956
|
+
};
|
|
957
|
+
}
|
|
1089
958
|
function previewResultJson(result, exposeCandidate, writeRequest = { mode: "preview", target: null }, writeResult = null) {
|
|
1090
959
|
return {
|
|
1091
960
|
changed: exposeCandidate ? result.changed : false,
|
|
@@ -1116,8 +985,7 @@ async function readMutationRequest(source) {
|
|
|
1116
985
|
}
|
|
1117
986
|
}
|
|
1118
987
|
async function runMutation(resource, action, args) {
|
|
1119
|
-
const
|
|
1120
|
-
const parsed = parseOptions(args, config.values, config.flags, config.repeatable);
|
|
988
|
+
const parsed = parseCommandOptions(`${resource}.${action}`, args);
|
|
1121
989
|
const format = outputFormat(parsed.values.get("format"));
|
|
1122
990
|
const color = colorMode(parsed.values.get("color"), format);
|
|
1123
991
|
const maxDiagnostics = boundedInteger(parsed.values.get("max-diagnostics"), "max-diagnostics", 100, 1, 1000);
|
|
@@ -1125,9 +993,9 @@ async function runMutation(resource, action, args) {
|
|
|
1125
993
|
let sourceOperand;
|
|
1126
994
|
let writeRequest;
|
|
1127
995
|
let mutation;
|
|
1128
|
-
if (resource === "
|
|
996
|
+
if (resource === "batch") {
|
|
1129
997
|
if (parsed.positionals.length !== 1) {
|
|
1130
|
-
throw new UsageError("
|
|
998
|
+
throw new UsageError("batch apply requires exactly one <file>");
|
|
1131
999
|
}
|
|
1132
1000
|
sourceOperand = parsed.positionals[0];
|
|
1133
1001
|
writeRequest = editingWriteRequest(parsed, sourceOperand);
|
|
@@ -1152,9 +1020,11 @@ async function runMutation(resource, action, args) {
|
|
|
1152
1020
|
? projectMutationFromOptions(parsed)
|
|
1153
1021
|
: resource === "task"
|
|
1154
1022
|
? taskMutationFromOptions(action, parsed)
|
|
1155
|
-
: resource === "
|
|
1156
|
-
?
|
|
1157
|
-
:
|
|
1023
|
+
: resource === "gate"
|
|
1024
|
+
? gateMutationFromOptions(action, parsed)
|
|
1025
|
+
: resource === "milestone"
|
|
1026
|
+
? milestoneMutationFromOptions(action, parsed)
|
|
1027
|
+
: resourceMutationFromOptions(action, parsed);
|
|
1158
1028
|
sourceOperand = parsed.positionals[0];
|
|
1159
1029
|
writeRequest = editingWriteRequest(parsed, sourceOperand);
|
|
1160
1030
|
}
|
|
@@ -1171,7 +1041,7 @@ async function runMutation(resource, action, args) {
|
|
|
1171
1041
|
originalLabel: source,
|
|
1172
1042
|
updatedLabel: "candidate",
|
|
1173
1043
|
};
|
|
1174
|
-
const result = resource === "
|
|
1044
|
+
const result = resource === "batch"
|
|
1175
1045
|
? planBatchMutation(input.text, mutation, mutationOptions)
|
|
1176
1046
|
: planMutation(input.text, mutation, mutationOptions);
|
|
1177
1047
|
const warningFailure = parsed.flags.has("warnings-as-errors") &&
|
|
@@ -1193,6 +1063,7 @@ async function runMutation(resource, action, args) {
|
|
|
1193
1063
|
if (format === "json") {
|
|
1194
1064
|
writeJson({
|
|
1195
1065
|
schema_version: "Perttool.MutationResult.v1",
|
|
1066
|
+
cli_contract_version: 3,
|
|
1196
1067
|
tool_version: TOOL_VERSION,
|
|
1197
1068
|
operation,
|
|
1198
1069
|
ok,
|
|
@@ -1247,7 +1118,7 @@ function renderAdvanceSummary(details) {
|
|
|
1247
1118
|
].join("\n");
|
|
1248
1119
|
}
|
|
1249
1120
|
async function runAdvance(args) {
|
|
1250
|
-
const parsed =
|
|
1121
|
+
const parsed = parseCommandOptions("dag.advance", args);
|
|
1251
1122
|
if (parsed.positionals.length !== 1) {
|
|
1252
1123
|
throw new UsageError("dag advance requires exactly one <file>");
|
|
1253
1124
|
}
|
|
@@ -1288,6 +1159,7 @@ async function runAdvance(args) {
|
|
|
1288
1159
|
if (format === "json") {
|
|
1289
1160
|
writeJson({
|
|
1290
1161
|
schema_version: "Perttool.MutationResult.v1",
|
|
1162
|
+
cli_contract_version: 3,
|
|
1291
1163
|
tool_version: TOOL_VERSION,
|
|
1292
1164
|
operation: "dag.advance",
|
|
1293
1165
|
ok,
|
|
@@ -1568,14 +1440,7 @@ function renderAnalysisText(result) {
|
|
|
1568
1440
|
return `${lines.join("\n")}\n`;
|
|
1569
1441
|
}
|
|
1570
1442
|
async function runAnalyze(args) {
|
|
1571
|
-
const parsed =
|
|
1572
|
-
"schedule",
|
|
1573
|
-
"max-paths",
|
|
1574
|
-
"precision",
|
|
1575
|
-
"max-diagnostics",
|
|
1576
|
-
"format",
|
|
1577
|
-
"color",
|
|
1578
|
-
]), new Set(["warnings-as-errors"]), new Set(["capacity"]));
|
|
1443
|
+
const parsed = parseCommandOptions("dag.analyze", args);
|
|
1579
1444
|
if (parsed.positionals.length !== 1) {
|
|
1580
1445
|
throw new UsageError("dag analyze requires exactly one <file>");
|
|
1581
1446
|
}
|
|
@@ -1609,6 +1474,7 @@ async function runAnalyze(args) {
|
|
|
1609
1474
|
if (format === "json") {
|
|
1610
1475
|
writeJson({
|
|
1611
1476
|
schema_version: "Perttool.AnalysisResult.v2",
|
|
1477
|
+
cli_contract_version: 3,
|
|
1612
1478
|
tool_version: TOOL_VERSION,
|
|
1613
1479
|
operation: "dag.analyze",
|
|
1614
1480
|
ok,
|
|
@@ -1668,15 +1534,7 @@ function renderConversionLoss(loss, source, color) {
|
|
|
1668
1534
|
}, source, color);
|
|
1669
1535
|
}
|
|
1670
1536
|
async function runRender(args) {
|
|
1671
|
-
const parsed =
|
|
1672
|
-
"to",
|
|
1673
|
-
"profile",
|
|
1674
|
-
"analysis",
|
|
1675
|
-
"out",
|
|
1676
|
-
"max-diagnostics",
|
|
1677
|
-
"format",
|
|
1678
|
-
"color",
|
|
1679
|
-
]), new Set(["strict-loss", "warnings-as-errors"]), new Set(["capacity"]));
|
|
1537
|
+
const parsed = parseCommandOptions("dag.render", args);
|
|
1680
1538
|
if (parsed.positionals.length !== 1) {
|
|
1681
1539
|
throw new UsageError("dag render requires exactly one <file>");
|
|
1682
1540
|
}
|
|
@@ -1728,6 +1586,7 @@ async function runRender(args) {
|
|
|
1728
1586
|
if (format === "json") {
|
|
1729
1587
|
writeJson({
|
|
1730
1588
|
schema_version: "Perttool.ExportResult.v1",
|
|
1589
|
+
cli_contract_version: 3,
|
|
1731
1590
|
tool_version: TOOL_VERSION,
|
|
1732
1591
|
operation: "dag.render",
|
|
1733
1592
|
ok,
|
|
@@ -1778,7 +1637,7 @@ async function runRender(args) {
|
|
|
1778
1637
|
return strictFailure ? 4 : ok ? 0 : 1;
|
|
1779
1638
|
}
|
|
1780
1639
|
async function runImport(args) {
|
|
1781
|
-
const parsed =
|
|
1640
|
+
const parsed = parseCommandOptions("dag.import", args);
|
|
1782
1641
|
if (parsed.positionals.length !== 1) {
|
|
1783
1642
|
throw new UsageError("dag import requires exactly one <file>");
|
|
1784
1643
|
}
|
|
@@ -1819,6 +1678,7 @@ async function runImport(args) {
|
|
|
1819
1678
|
if (format === "json") {
|
|
1820
1679
|
writeJson({
|
|
1821
1680
|
schema_version: "Perttool.ImportResult.v1",
|
|
1681
|
+
cli_contract_version: 3,
|
|
1822
1682
|
tool_version: TOOL_VERSION,
|
|
1823
1683
|
operation: "dag.import",
|
|
1824
1684
|
ok,
|
|
@@ -2077,7 +1937,7 @@ function renderNextText(result) {
|
|
|
2077
1937
|
return `${lines.join("\n")}\n`;
|
|
2078
1938
|
}
|
|
2079
1939
|
async function runNext(args) {
|
|
2080
|
-
const parsed =
|
|
1940
|
+
const parsed = parseCommandOptions("dag.next", args);
|
|
2081
1941
|
if (parsed.positionals.length !== 1) {
|
|
2082
1942
|
throw new UsageError("dag next requires exactly one <file>");
|
|
2083
1943
|
}
|
|
@@ -2108,6 +1968,7 @@ async function runNext(args) {
|
|
|
2108
1968
|
if (format === "json") {
|
|
2109
1969
|
writeJson({
|
|
2110
1970
|
schema_version: "Perttool.CliError.v1",
|
|
1971
|
+
cli_contract_version: 3,
|
|
2111
1972
|
tool_version: TOOL_VERSION,
|
|
2112
1973
|
operation: "dag.next",
|
|
2113
1974
|
ok: false,
|
|
@@ -2128,6 +1989,7 @@ async function runNext(args) {
|
|
|
2128
1989
|
if (format === "json") {
|
|
2129
1990
|
writeJson({
|
|
2130
1991
|
schema_version: "Perttool.NextResult.v3",
|
|
1992
|
+
cli_contract_version: 3,
|
|
2131
1993
|
recommendation_interface_version: 1,
|
|
2132
1994
|
tool_version: TOOL_VERSION,
|
|
2133
1995
|
operation: "dag.next",
|
|
@@ -2152,65 +2014,56 @@ async function runNext(args) {
|
|
|
2152
2014
|
}
|
|
2153
2015
|
return ok ? 0 : 1;
|
|
2154
2016
|
}
|
|
2155
|
-
function
|
|
2156
|
-
const
|
|
2157
|
-
if (result.topics.length > 0) {
|
|
2158
|
-
lines.push("", "Topics:");
|
|
2159
|
-
for (const topic of result.topics)
|
|
2160
|
-
lines.push(` ${topic.id.padEnd(12)} ${topic.summary}`);
|
|
2161
|
-
}
|
|
2162
|
-
for (const section of result.sections) {
|
|
2163
|
-
lines.push("", section.title, section.body);
|
|
2164
|
-
}
|
|
2165
|
-
if (result.syntax.length > 0)
|
|
2166
|
-
lines.push("", "Syntax:", ...result.syntax.map((line) => ` ${line}`));
|
|
2167
|
-
if (result.examples.length > 0) {
|
|
2168
|
-
lines.push("", "Examples:", ...result.examples.map((example) => ` ${example.id}: ${example.text}`));
|
|
2169
|
-
}
|
|
2170
|
-
if (result.related.length > 0)
|
|
2171
|
-
lines.push("", `Related: ${result.related.join(", ")}`);
|
|
2172
|
-
return `${lines.join("\n")}\n`;
|
|
2173
|
-
}
|
|
2174
|
-
function runHelp(args) {
|
|
2175
|
-
const parsed = parseOptions(args, new Set(["level", "format", "color"]), new Set());
|
|
2017
|
+
function runGuide(args) {
|
|
2018
|
+
const parsed = parseCommandOptions("guide", args);
|
|
2176
2019
|
if (parsed.positionals.length > 2) {
|
|
2177
|
-
throw new UsageError("
|
|
2020
|
+
throw new UsageError("guide accepts at most <topic> <subtopic>");
|
|
2178
2021
|
}
|
|
2179
2022
|
const format = outputFormat(parsed.values.get("format"));
|
|
2180
2023
|
colorMode(parsed.values.get("color"), format);
|
|
2181
2024
|
const topicId = parsed.positionals.length === 0 ? null : parsed.positionals.join(".");
|
|
2182
2025
|
const level = helpLevel(parsed.values.get("level"), topicId !== null);
|
|
2183
|
-
const result =
|
|
2026
|
+
const result = getGuide(topicId, level);
|
|
2184
2027
|
if (format === "json") {
|
|
2185
|
-
|
|
2186
|
-
schema_version: "Perttool.HelpResult.v1",
|
|
2187
|
-
tool_version: TOOL_VERSION,
|
|
2188
|
-
operation: "dsl.help",
|
|
2189
|
-
ok: result.ok,
|
|
2190
|
-
diagnostics: result.diagnostics.map(jsonDiagnostic),
|
|
2191
|
-
topic_id: result.topicId,
|
|
2192
|
-
level: result.level,
|
|
2193
|
-
title: result.title,
|
|
2194
|
-
summary: result.summary,
|
|
2195
|
-
sections: result.sections,
|
|
2196
|
-
syntax: result.syntax,
|
|
2197
|
-
examples: result.examples,
|
|
2198
|
-
related: result.related,
|
|
2199
|
-
topics: result.topics,
|
|
2200
|
-
});
|
|
2028
|
+
process.stdout.write(serializeGuideResult(result));
|
|
2201
2029
|
}
|
|
2202
|
-
else
|
|
2203
|
-
|
|
2030
|
+
else {
|
|
2031
|
+
const rendered = renderGuideResult(result);
|
|
2032
|
+
if (result.ok) {
|
|
2033
|
+
process.stdout.write(rendered);
|
|
2034
|
+
}
|
|
2035
|
+
else {
|
|
2036
|
+
process.stderr.write(rendered);
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return result.ok ? 0 : 1;
|
|
2040
|
+
}
|
|
2041
|
+
function runCommandHelp(args) {
|
|
2042
|
+
const parsed = parseCommandOptions("help", args);
|
|
2043
|
+
if (parsed.positionals.length > 2) {
|
|
2044
|
+
throw new UsageError("help accepts at most <resource> <action>");
|
|
2045
|
+
}
|
|
2046
|
+
const format = outputFormat(parsed.values.get("format"));
|
|
2047
|
+
const result = getCommandDiscovery({
|
|
2048
|
+
resource: parsed.positionals[0] ?? null,
|
|
2049
|
+
action: parsed.positionals[1] ?? null,
|
|
2050
|
+
});
|
|
2051
|
+
if (format === "json") {
|
|
2052
|
+
process.stdout.write(serializeCommandHelpResult(result));
|
|
2204
2053
|
}
|
|
2205
2054
|
else {
|
|
2206
|
-
|
|
2207
|
-
|
|
2055
|
+
const rendered = renderCommandHelpResult(result);
|
|
2056
|
+
if (result.ok) {
|
|
2057
|
+
process.stdout.write(rendered);
|
|
2058
|
+
}
|
|
2059
|
+
else {
|
|
2060
|
+
process.stderr.write(rendered);
|
|
2208
2061
|
}
|
|
2209
2062
|
}
|
|
2210
2063
|
return result.ok ? 0 : 1;
|
|
2211
2064
|
}
|
|
2212
2065
|
function runAgentHelp(args) {
|
|
2213
|
-
const parsed =
|
|
2066
|
+
const parsed = parseCommandOptions("agent.help", args);
|
|
2214
2067
|
if (parsed.positionals.length > 2) {
|
|
2215
2068
|
throw new UsageError("agent help accepts at most <provider> <surface>");
|
|
2216
2069
|
}
|
|
@@ -2225,7 +2078,13 @@ function runAgentHelp(args) {
|
|
|
2225
2078
|
level,
|
|
2226
2079
|
});
|
|
2227
2080
|
if (format === "json") {
|
|
2228
|
-
|
|
2081
|
+
const projected = agentGuidanceResultToJson(result);
|
|
2082
|
+
const { schema_version: schemaVersion, ...payload } = projected;
|
|
2083
|
+
writeJson({
|
|
2084
|
+
schema_version: schemaVersion,
|
|
2085
|
+
cli_contract_version: 3,
|
|
2086
|
+
...payload,
|
|
2087
|
+
});
|
|
2229
2088
|
}
|
|
2230
2089
|
else {
|
|
2231
2090
|
if (result.ok) {
|
|
@@ -2245,74 +2104,88 @@ function runAgentHelp(args) {
|
|
|
2245
2104
|
}
|
|
2246
2105
|
return agentGuidanceExitCode(result.diagnostics);
|
|
2247
2106
|
}
|
|
2107
|
+
async function dispatchCommand(descriptor, args) {
|
|
2108
|
+
switch (descriptor.operation) {
|
|
2109
|
+
case "help":
|
|
2110
|
+
return runCommandHelp(args);
|
|
2111
|
+
case "guide":
|
|
2112
|
+
return runGuide(args);
|
|
2113
|
+
case "document.check":
|
|
2114
|
+
return runCheck(args);
|
|
2115
|
+
case "document.format":
|
|
2116
|
+
return runFormat(args);
|
|
2117
|
+
case "agent.help":
|
|
2118
|
+
return runAgentHelp(args);
|
|
2119
|
+
case "project.init":
|
|
2120
|
+
return runProjectInit(args);
|
|
2121
|
+
case "project.show":
|
|
2122
|
+
return runProjectShow(args);
|
|
2123
|
+
case "dag.analyze":
|
|
2124
|
+
return runAnalyze(args);
|
|
2125
|
+
case "dag.next":
|
|
2126
|
+
return runNext(args);
|
|
2127
|
+
case "dag.advance":
|
|
2128
|
+
return runAdvance(args);
|
|
2129
|
+
case "dag.render":
|
|
2130
|
+
return runRender(args);
|
|
2131
|
+
case "dag.import":
|
|
2132
|
+
return runImport(args);
|
|
2133
|
+
}
|
|
2134
|
+
if (descriptor.path.length === 2
|
|
2135
|
+
&& (descriptor.operation === "project.set"
|
|
2136
|
+
|| descriptor.operation === "batch.apply"
|
|
2137
|
+
|| /^(?:task|gate|milestone|resource)\.(?:add|set|remove|finish)$/.test(descriptor.operation))) {
|
|
2138
|
+
const [resource, action] = descriptor.path;
|
|
2139
|
+
return runMutation(resource, action, args);
|
|
2140
|
+
}
|
|
2141
|
+
throw new Error(`no Contract 3 handler for ${descriptor.operation}`);
|
|
2142
|
+
}
|
|
2143
|
+
function emitCommandUsage(error, json) {
|
|
2144
|
+
if (json) {
|
|
2145
|
+
process.stdout.write(serializeCommandUsageError(error));
|
|
2146
|
+
}
|
|
2147
|
+
else {
|
|
2148
|
+
process.stderr.write(renderCommandUsageError(error));
|
|
2149
|
+
}
|
|
2150
|
+
return 2;
|
|
2151
|
+
}
|
|
2248
2152
|
async function main(argv) {
|
|
2249
2153
|
if (argv.length === 1 && argv[0] === "--version") {
|
|
2250
2154
|
process.stdout.write(`perttool ${TOOL_VERSION}\n`);
|
|
2251
2155
|
return 0;
|
|
2252
2156
|
}
|
|
2253
2157
|
if (argv.length === 1 && argv[0] === "--help") {
|
|
2254
|
-
|
|
2255
|
-
return 0;
|
|
2158
|
+
return runCommandHelp([]);
|
|
2256
2159
|
}
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
["mutation", new Set(["apply"])],
|
|
2267
|
-
]);
|
|
2268
|
-
const isMutationCommand = entityActions.get(resource)?.has(action) === true;
|
|
2269
|
-
if (argv.length === 3 &&
|
|
2270
|
-
argv[2] === "--help" &&
|
|
2271
|
-
((resource === "agent" && action === "help") ||
|
|
2272
|
-
(resource === "dsl" && ["check", "format", "help"].includes(action)) ||
|
|
2273
|
-
(resource === "project" && action === "show") ||
|
|
2274
|
-
(resource === "dag" && ["analyze", "next", "advance", "render", "import"].includes(action)) ||
|
|
2275
|
-
isMutationCommand)) {
|
|
2276
|
-
process.stdout.write(`${commandHelp(resource, action)}\n`);
|
|
2277
|
-
return 0;
|
|
2278
|
-
}
|
|
2279
|
-
if (resource === "dag" && action === "analyze") {
|
|
2280
|
-
return runAnalyze(argv.slice(2));
|
|
2281
|
-
}
|
|
2282
|
-
if (resource === "dag" && action === "next") {
|
|
2283
|
-
return runNext(argv.slice(2));
|
|
2284
|
-
}
|
|
2285
|
-
if (resource === "dag" && action === "advance") {
|
|
2286
|
-
return runAdvance(argv.slice(2));
|
|
2287
|
-
}
|
|
2288
|
-
if (resource === "dag" && action === "render") {
|
|
2289
|
-
return runRender(argv.slice(2));
|
|
2290
|
-
}
|
|
2291
|
-
if (resource === "dag" && action === "import") {
|
|
2292
|
-
return runImport(argv.slice(2));
|
|
2293
|
-
}
|
|
2294
|
-
if (resource === "agent" && action === "help") {
|
|
2295
|
-
return runAgentHelp(argv.slice(2));
|
|
2296
|
-
}
|
|
2297
|
-
if (resource === "project" && action === "show") {
|
|
2298
|
-
return runProjectShow(argv.slice(2));
|
|
2160
|
+
const validation = validateCommandInvocation(argv);
|
|
2161
|
+
if (!validation.ok) {
|
|
2162
|
+
if (jsonRequested(argv)) {
|
|
2163
|
+
process.stdout.write(serializeCommandUsageError(validation.error));
|
|
2164
|
+
}
|
|
2165
|
+
else {
|
|
2166
|
+
process.stderr.write(renderCommandUsageError(validation.error));
|
|
2167
|
+
}
|
|
2168
|
+
return 2;
|
|
2299
2169
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2170
|
+
const descriptor = validation.descriptor;
|
|
2171
|
+
if (validation.helpAlias) {
|
|
2172
|
+
return runCommandHelp([...descriptor.path]);
|
|
2302
2173
|
}
|
|
2303
|
-
|
|
2304
|
-
return
|
|
2174
|
+
try {
|
|
2175
|
+
return await dispatchCommand(descriptor, argv.slice(descriptor.path.length));
|
|
2305
2176
|
}
|
|
2306
|
-
|
|
2307
|
-
|
|
2177
|
+
catch (error) {
|
|
2178
|
+
if (error instanceof UsageError) {
|
|
2179
|
+
return emitCommandUsage(handlerCommandUsageError(descriptor, error.message), jsonRequested(argv));
|
|
2180
|
+
}
|
|
2181
|
+
throw error;
|
|
2308
2182
|
}
|
|
2309
|
-
return action === "check" ? runCheck(argv.slice(2)) : runHelp(argv.slice(2));
|
|
2310
2183
|
}
|
|
2311
2184
|
const args = process.argv.slice(2);
|
|
2312
2185
|
try {
|
|
2313
2186
|
process.exitCode = await main(args);
|
|
2314
2187
|
}
|
|
2315
2188
|
catch (error) {
|
|
2316
|
-
process.exitCode = cliError(error instanceof Error ? error : new Error(String(error)),
|
|
2189
|
+
process.exitCode = cliError(error instanceof Error ? error : new Error(String(error)), 70, null, jsonRequested(args));
|
|
2317
2190
|
}
|
|
2318
2191
|
//# sourceMappingURL=cli.js.map
|