perttool 0.1.0-alpha.2 → 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.
Files changed (99) hide show
  1. package/CHANGELOG.md +78 -28
  2. package/README.md +173 -95
  3. package/dist/application/agent-help.d.ts +3 -0
  4. package/dist/application/agent-help.d.ts.map +1 -0
  5. package/dist/application/agent-help.js +5 -0
  6. package/dist/application/agent-help.js.map +1 -0
  7. package/dist/application/analyze.js +5 -5
  8. package/dist/application/analyze.js.map +1 -1
  9. package/dist/application/init.d.ts +42 -0
  10. package/dist/application/init.d.ts.map +1 -0
  11. package/dist/application/init.js +217 -0
  12. package/dist/application/init.js.map +1 -0
  13. package/dist/application/mutate.d.ts.map +1 -1
  14. package/dist/application/mutate.js +28 -15
  15. package/dist/application/mutate.js.map +1 -1
  16. package/dist/application/project.d.ts +25 -0
  17. package/dist/application/project.d.ts.map +1 -0
  18. package/dist/application/project.js +54 -0
  19. package/dist/application/project.js.map +1 -0
  20. package/dist/cli.js +419 -331
  21. package/dist/cli.js.map +1 -1
  22. package/dist/command/discovery.d.ts +30 -0
  23. package/dist/command/discovery.d.ts.map +1 -0
  24. package/dist/command/discovery.js +625 -0
  25. package/dist/command/discovery.js.map +1 -0
  26. package/dist/command/registry.d.ts +84 -0
  27. package/dist/command/registry.d.ts.map +1 -0
  28. package/dist/command/registry.js +951 -0
  29. package/dist/command/registry.js.map +1 -0
  30. package/dist/command/usage.d.ts +42 -0
  31. package/dist/command/usage.d.ts.map +1 -0
  32. package/dist/command/usage.js +337 -0
  33. package/dist/command/usage.js.map +1 -0
  34. package/dist/conversion/mermaid-import.js +56 -56
  35. package/dist/conversion/mermaid-import.js.map +1 -1
  36. package/dist/conversion/mermaid.js +1 -1
  37. package/dist/conversion/mermaid.js.map +1 -1
  38. package/dist/guidance/profile.d.ts +6 -0
  39. package/dist/guidance/profile.d.ts.map +1 -0
  40. package/dist/guidance/profile.js +1158 -0
  41. package/dist/guidance/profile.js.map +1 -0
  42. package/dist/guidance/projection.d.ts +16 -0
  43. package/dist/guidance/projection.d.ts.map +1 -0
  44. package/dist/guidance/projection.js +276 -0
  45. package/dist/guidance/projection.js.map +1 -0
  46. package/dist/guidance/query.d.ts +4 -0
  47. package/dist/guidance/query.d.ts.map +1 -0
  48. package/dist/guidance/query.js +178 -0
  49. package/dist/guidance/query.js.map +1 -0
  50. package/dist/guidance/text.d.ts +4 -0
  51. package/dist/guidance/text.d.ts.map +1 -0
  52. package/dist/guidance/text.js +118 -0
  53. package/dist/guidance/text.js.map +1 -0
  54. package/dist/guidance/types.d.ts +227 -0
  55. package/dist/guidance/types.d.ts.map +1 -0
  56. package/dist/guidance/types.js +54 -0
  57. package/dist/guidance/types.js.map +1 -0
  58. package/dist/guidance/validator.d.ts +8 -0
  59. package/dist/guidance/validator.d.ts.map +1 -0
  60. package/dist/guidance/validator.js +383 -0
  61. package/dist/guidance/validator.js.map +1 -0
  62. package/dist/help/guide.d.ts +12 -0
  63. package/dist/help/guide.d.ts.map +1 -0
  64. package/dist/help/guide.js +103 -0
  65. package/dist/help/guide.js.map +1 -0
  66. package/dist/help/registry.d.ts.map +1 -1
  67. package/dist/help/registry.js +76 -67
  68. package/dist/help/registry.js.map +1 -1
  69. package/dist/index.d.ts +21 -1
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +13 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/io/safe-write.js +17 -17
  74. package/dist/io/safe-write.js.map +1 -1
  75. package/dist/mutation/gate.d.ts +5 -0
  76. package/dist/mutation/gate.d.ts.map +1 -0
  77. package/dist/mutation/gate.js +156 -0
  78. package/dist/mutation/gate.js.map +1 -0
  79. package/dist/mutation/milestone.js +26 -26
  80. package/dist/mutation/milestone.js.map +1 -1
  81. package/dist/mutation/project.d.ts +5 -0
  82. package/dist/mutation/project.d.ts.map +1 -0
  83. package/dist/mutation/project.js +148 -0
  84. package/dist/mutation/project.js.map +1 -0
  85. package/dist/mutation/resource.js +22 -22
  86. package/dist/mutation/resource.js.map +1 -1
  87. package/dist/mutation/task.js +40 -40
  88. package/dist/mutation/task.js.map +1 -1
  89. package/dist/mutation/types.d.ts +46 -1
  90. package/dist/mutation/types.d.ts.map +1 -1
  91. package/dist/parser/document-parser.js +17 -17
  92. package/dist/parser/document-parser.js.map +1 -1
  93. package/dist/semantic/validator.js +41 -41
  94. package/dist/semantic/validator.js.map +1 -1
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.d.ts.map +1 -1
  97. package/dist/version.js +1 -1
  98. package/dist/version.js.map +1 -1
  99. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -5,11 +5,19 @@ 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";
11
+ import { getProjectMetadata, } from "./application/project.js";
12
+ import { getAgentHelp } from "./application/agent-help.js";
10
13
  import { exportMermaid, } from "./conversion/mermaid.js";
11
14
  import { importMermaid } from "./conversion/mermaid-import.js";
12
- import { getHelp } from "./help/registry.js";
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";
20
+ import { agentGuidanceExitCode, renderAgentGuidanceText, } from "./guidance/text.js";
13
21
  import { documentContentFromBytes, readDocumentFile, } from "./io/document-file.js";
14
22
  import { createArtifactFile, createDocumentFile, replaceDocumentFile, SafeWriteConflictError, SafeWriteVerificationError, } from "./io/safe-write.js";
15
23
  import { formatDecimal } from "./model/rational.js";
@@ -21,159 +29,6 @@ import { TOOL_VERSION } from "./version.js";
21
29
  class UsageError extends Error {
22
30
  code = "PTCLI-001";
23
31
  }
24
- function topLevelHelp() {
25
- return [
26
- "perttool - document-based PERT/CPM task management",
27
- "",
28
- "Usage:",
29
- " perttool --version",
30
- " perttool --help",
31
- " perttool dsl check <file> [--format text|json]",
32
- " perttool dsl format <file> [--check] [--diff] [--format text|json]",
33
- " perttool dsl help [topic [subtopic]] [--level index|quick|detail] [--format text|json]",
34
- " perttool dag analyze <file> [--schedule precedence|resource|both] [--format text|json]",
35
- " perttool dag next <file> [--capacity <resource-id>=<integer>] [--format text|json]",
36
- " perttool dag advance <file> [--diff] [--write | --out <path>] [--format text|json]",
37
- " perttool dag render <file> --to mermaid [--profile perttool|plain] [--format text|json]",
38
- " perttool dag import <file> --from mermaid [--strict-loss] [--out <path>] [--format text|json]",
39
- " perttool task add|set|remove|finish ...",
40
- " perttool milestone add|set|remove ...",
41
- " perttool resource add|set|remove ...",
42
- " perttool mutation apply <file> --request <json-file|-> [--diff] [--format text|json]",
43
- "",
44
- "Format and mutation commands preview by default; use --write or --out for explicit writes.",
45
- ].join("\n");
46
- }
47
- function commandHelp(resource, action) {
48
- if (resource === "dsl" && action === "check") {
49
- return [
50
- "Usage: perttool dsl check <file>",
51
- " [--warnings-as-errors]",
52
- " [--max-diagnostics <integer>]",
53
- " [--format text|json]",
54
- " [--color auto|always|never]",
55
- ].join("\n");
56
- }
57
- if (resource === "dsl" && action === "format")
58
- return [
59
- "Usage: perttool dsl format <file>",
60
- " [--check] [--diff]",
61
- " [--write [--expect-digest <digest>] | --out <path>]",
62
- " [--max-diagnostics <integer>] [--warnings-as-errors]",
63
- " [--format text|json] [--color auto|always|never]",
64
- ].join("\n");
65
- if (resource === "dsl" && action === "help")
66
- return [
67
- "Usage: perttool dsl help [topic [subtopic]]",
68
- " [--level index|quick|detail]",
69
- " [--format text|json]",
70
- " [--color auto|always|never]",
71
- ].join("\n");
72
- if (resource === "dag" && action === "analyze")
73
- return [
74
- "Usage: perttool dag analyze <file>",
75
- " [--schedule precedence|resource|both]",
76
- " [--capacity <resource-id>=<integer>]...",
77
- " [--max-paths <integer>] [--precision <integer>]",
78
- " [--max-diagnostics <integer>]",
79
- " [--warnings-as-errors]",
80
- " [--format text|json] [--color auto|always|never]",
81
- ].join("\n");
82
- if (resource === "dag" && action === "next")
83
- return [
84
- "Usage: perttool dag next <file>",
85
- " [--capacity <resource-id>=<integer>]...",
86
- " [--explain-depth <integer>] [--precision <integer>]",
87
- " [--max-diagnostics <integer>]",
88
- " [--warnings-as-errors]",
89
- " [--format text|json] [--color auto|always|never]",
90
- "Output: Perttool.NextResult.v3 with a complete recommendation graph in JSON.",
91
- "Consumers must inspect schema_version before using recommendation authority.",
92
- ].join("\n");
93
- if (resource === "dag" && action === "advance")
94
- return [
95
- "Usage: perttool dag advance <file>",
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 === "render")
101
- return [
102
- "Usage: perttool dag render <file> --to mermaid",
103
- " [--profile perttool|plain] [--analysis none|precedence|resource|both]",
104
- " [--capacity <resource-id>=<integer>]... [--strict-loss] [--out <path>]",
105
- " [--max-diagnostics <integer>] [--warnings-as-errors]",
106
- " [--format text|json] [--color auto|always|never]",
107
- ].join("\n");
108
- if (resource === "dag" && action === "import")
109
- return [
110
- "Usage: perttool dag import <file> --from mermaid",
111
- " [--strict-loss] [--out <path>]",
112
- " [--max-diagnostics <integer>] [--warnings-as-errors]",
113
- " [--format text|json] [--color auto|always|never]",
114
- ].join("\n");
115
- const preview = " [--diff] [--write [--expect-digest <digest>] | --out <path>] [--max-diagnostics <integer>] [--warnings-as-errors] [--format text|json] [--color auto|always|never]";
116
- if (resource === "task" && action === "add")
117
- return [
118
- "Usage: perttool task add <file> <id> <from> <to>",
119
- " --title <text> (--duration <duration> | --optimistic <duration> --most-likely <duration> --pessimistic <duration>)",
120
- " [--description <text>] [--status planned|active|blocked|done] [--priority <integer>]",
121
- " [--owner <text>] [--blocked-reason <text>] [--source <text>] [--tag <tag>]... [--require <resource-id>=<integer>]...",
122
- preview,
123
- ].join("\n");
124
- if (resource === "task" && action === "set")
125
- return [
126
- "Usage: perttool task set <file> <id> [field options]",
127
- " [--from <id>] [--to <id>] [--title <text>] [--description <text>] [--duration <duration>]",
128
- " [--optimistic <duration> --most-likely <duration> --pessimistic <duration>]",
129
- " [--status planned|active|blocked|done] [--priority <integer>] [--owner <text>]",
130
- " [--blocked-reason <text>] [--source <text>] [--require <resource-id>=<integer>]...",
131
- " [--add-tag <tag>]... [--remove-tag <tag>]... [--remove-require <resource-id>]... [--clear <field>]...",
132
- preview,
133
- ].join("\n");
134
- if (resource === "task")
135
- return [
136
- `Usage: perttool task ${action} <file> <id>`,
137
- preview,
138
- ].join("\n");
139
- if (resource === "milestone" && action === "add")
140
- return [
141
- "Usage: perttool milestone add <file> <id> --title <text>",
142
- " [--description <text>] [--state planned|reached] [--tag <tag>]...",
143
- preview,
144
- ].join("\n");
145
- if (resource === "milestone" && action === "set")
146
- return [
147
- "Usage: perttool milestone set <file> <id> [--title <text>] [--description <text>] [--state planned|reached]",
148
- " [--add-tag <tag>]... [--remove-tag <tag>]... [--clear description|state|tags]...",
149
- preview,
150
- ].join("\n");
151
- if (resource === "milestone")
152
- return [
153
- `Usage: perttool milestone ${action} <file> <id>`,
154
- preview,
155
- ].join("\n");
156
- if (resource === "resource" && action === "add")
157
- return [
158
- "Usage: perttool resource add <file> <id> --title <text> --capacity <integer>",
159
- " [--description <text>]",
160
- preview,
161
- ].join("\n");
162
- if (resource === "resource" && action === "set")
163
- return [
164
- "Usage: perttool resource set <file> <id> [--title <text>] [--description <text>] [--capacity <integer>] [--clear description]",
165
- preview,
166
- ].join("\n");
167
- if (resource === "resource")
168
- return [
169
- `Usage: perttool resource ${action} <file> <id>`,
170
- preview,
171
- ].join("\n");
172
- return [
173
- "Usage: perttool mutation apply <file> --request <json-file|->",
174
- preview,
175
- ].join("\n");
176
- }
177
32
  function parseOptions(args, valueOptions, flagOptions, repeatableOptions = new Set()) {
178
33
  const positionals = [];
179
34
  const values = new Map();
@@ -223,6 +78,14 @@ function parseOptions(args, valueOptions, flagOptions, repeatableOptions = new S
223
78
  }
224
79
  return { positionals, values, repeatedValues, flags };
225
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
+ }
226
89
  function outputFormat(value) {
227
90
  if (value === undefined || value === "text")
228
91
  return "text";
@@ -272,7 +135,8 @@ function jsonDiagnostic(diagnostic) {
272
135
  message: related.message,
273
136
  span: jsonSpan(related.span),
274
137
  })),
275
- help_topic: diagnostic.helpTopic ?? null,
138
+ help_topic: null,
139
+ guide_topic: diagnostic.helpTopic ?? null,
276
140
  expected_syntax: diagnostic.expectedSyntax ?? null,
277
141
  fixes: [],
278
142
  data: diagnostic.data ?? {},
@@ -298,7 +162,7 @@ function renderDiagnostic(diagnostic, source, mode) {
298
162
  }
299
163
  if (diagnostic.helpTopic !== undefined) {
300
164
  const [topic, subtopic] = diagnostic.helpTopic.split(".", 2);
301
- lines.push(` help: perttool dsl help ${topic}${subtopic === undefined ? "" : ` ${subtopic}`} --level quick`);
165
+ lines.push(` guide: perttool guide ${topic}${subtopic === undefined ? "" : ` ${subtopic}`} --level quick`);
302
166
  }
303
167
  return lines.join("\n");
304
168
  }
@@ -341,6 +205,7 @@ function cliError(error, exitCode, operation, json) {
341
205
  if (json) {
342
206
  writeJson({
343
207
  schema_version: "Perttool.CliError.v1",
208
+ cli_contract_version: 3,
344
209
  tool_version: TOOL_VERSION,
345
210
  operation,
346
211
  ok: false,
@@ -353,9 +218,9 @@ function cliError(error, exitCode, operation, json) {
353
218
  return exitCode;
354
219
  }
355
220
  async function runCheck(args) {
356
- const parsed = parseOptions(args, new Set(["format", "color", "max-diagnostics"]), new Set(["warnings-as-errors"]));
221
+ const parsed = parseCommandOptions("document.check", args);
357
222
  if (parsed.positionals.length !== 1) {
358
- throw new UsageError("dsl check requires exactly one <file>");
223
+ throw new UsageError("document check requires exactly one <file>");
359
224
  }
360
225
  const format = outputFormat(parsed.values.get("format"));
361
226
  const color = colorMode(parsed.values.get("color"), format);
@@ -367,7 +232,7 @@ async function runCheck(args) {
367
232
  input = await readDocument(sourceOperand);
368
233
  }
369
234
  catch (error) {
370
- return cliError(error instanceof Error ? error : new Error(String(error)), 3, "dsl.check", format === "json");
235
+ return cliError(error instanceof Error ? error : new Error(String(error)), 3, "document.check", format === "json");
371
236
  }
372
237
  const result = checkDocument(input.text, { maxDiagnostics });
373
238
  const warningsAsErrors = parsed.flags.has("warnings-as-errors");
@@ -376,8 +241,9 @@ async function runCheck(args) {
376
241
  if (format === "json") {
377
242
  writeJson({
378
243
  schema_version: "Perttool.CheckResult.v1",
244
+ cli_contract_version: 3,
379
245
  tool_version: TOOL_VERSION,
380
- operation: "dsl.check",
246
+ operation: "document.check",
381
247
  ok,
382
248
  document_id: result.documentId,
383
249
  source,
@@ -401,6 +267,137 @@ async function runCheck(args) {
401
267
  }
402
268
  return ok ? 0 : 1;
403
269
  }
270
+ function projectJson(project) {
271
+ return {
272
+ id: project.id,
273
+ version: project.version,
274
+ title: project.title,
275
+ description: project.description,
276
+ as_of: project.asOf,
277
+ duration_unit: project.durationUnit,
278
+ velocity: project.velocity,
279
+ finish: project.finish,
280
+ critical_epsilon: project.criticalEpsilon,
281
+ target_duration: project.targetDuration,
282
+ };
283
+ }
284
+ function renderProjectText(project) {
285
+ const optional = (value) => value ?? "-";
286
+ return [
287
+ `PROJECT ${project.id}`,
288
+ `VERSION ${project.version}`,
289
+ `TITLE ${JSON.stringify(project.title)}`,
290
+ `DESCRIPTION ${project.description === null ? "-" : JSON.stringify(project.description)}`,
291
+ `AS_OF ${optional(project.asOf)}`,
292
+ `DURATION_UNIT ${project.durationUnit}`,
293
+ `VELOCITY ${optional(project.velocity)}`,
294
+ `FINISH ${project.finish}`,
295
+ `CRITICAL_EPSILON ${optional(project.criticalEpsilon)}`,
296
+ `TARGET_DURATION ${optional(project.targetDuration)}`,
297
+ "",
298
+ ].join("\n");
299
+ }
300
+ async function runProjectShow(args) {
301
+ const parsed = parseCommandOptions("project.show", args);
302
+ if (parsed.positionals.length !== 1) {
303
+ throw new UsageError("project show requires exactly one <file>");
304
+ }
305
+ const format = outputFormat(parsed.values.get("format"));
306
+ const color = colorMode(parsed.values.get("color"), format);
307
+ const maxDiagnostics = boundedInteger(parsed.values.get("max-diagnostics"), "max-diagnostics", 100, 1, 1000);
308
+ const sourceOperand = parsed.positionals[0];
309
+ const source = sourceOperand === "-" ? "<stdin>" : sourceOperand;
310
+ let input;
311
+ try {
312
+ input = await readDocument(sourceOperand);
313
+ }
314
+ catch (error) {
315
+ return cliError(error instanceof Error ? error : new Error(String(error)), 3, "project.show", format === "json");
316
+ }
317
+ const result = getProjectMetadata(input.text, { maxDiagnostics });
318
+ const warningFailure = parsed.flags.has("warnings-as-errors") &&
319
+ (result.diagnosticsTruncated ||
320
+ result.diagnostics.some((diagnostic) => diagnostic.severity === "warning"));
321
+ const ok = result.ok && !warningFailure;
322
+ if (format === "json") {
323
+ writeJson({
324
+ schema_version: "Perttool.ProjectResult.v1",
325
+ cli_contract_version: 3,
326
+ tool_version: TOOL_VERSION,
327
+ operation: "project.show",
328
+ ok,
329
+ document_id: result.documentId,
330
+ source,
331
+ source_digest: input.digest,
332
+ diagnostics: result.diagnostics.map(jsonDiagnostic),
333
+ diagnostics_truncated: result.diagnosticsTruncated,
334
+ grammar_version: result.grammarVersion,
335
+ project: result.project === null ? null : projectJson(result.project),
336
+ });
337
+ }
338
+ else {
339
+ if (ok && result.project !== null) {
340
+ process.stdout.write(renderProjectText(result.project));
341
+ }
342
+ for (const diagnostic of result.diagnostics) {
343
+ process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
344
+ }
345
+ if (result.diagnosticsTruncated) {
346
+ process.stderr.write(`DIAGNOSTICS_TRUNCATED true limit=${maxDiagnostics}\n`);
347
+ }
348
+ }
349
+ return ok ? 0 : 1;
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
+ }
404
401
  function boundedInteger(raw, option, defaultValue, minimum, maximum) {
405
402
  if (raw === undefined)
406
403
  return defaultValue;
@@ -438,14 +435,6 @@ function capacityOverrides(values) {
438
435
  }
439
436
  return overrides;
440
437
  }
441
- const mutationCommonValueOptions = [
442
- "format",
443
- "color",
444
- "max-diagnostics",
445
- "out",
446
- "expect-digest",
447
- ];
448
- const mutationCommonFlagOptions = ["diff", "write", "warnings-as-errors"];
449
438
  function editingWriteRequest(parsed, sourceOperand, check = false) {
450
439
  const write = parsed.flags.has("write");
451
440
  const out = parsed.values.get("out");
@@ -486,12 +475,12 @@ function assertExpectedDigest(request, initialDigest) {
486
475
  if (request.mode === "in_place" &&
487
476
  request.expectedDigest !== undefined &&
488
477
  request.expectedDigest !== initialDigest) {
489
- throw new SafeWriteConflictError("expected_digest_mismatch", "--expect-digestinitial document digestと一致しません");
478
+ throw new SafeWriteConflictError("expected_digest_mismatch", "--expect-digest does not match the initial document digest");
490
479
  }
491
480
  }
492
481
  async function commitCandidate(request, candidateText, initialDigest) {
493
482
  if (candidateText === null) {
494
- throw new SafeWriteVerificationError("invalid_candidate", "successful editing resultcandidate textがありません");
483
+ throw new SafeWriteVerificationError("invalid_candidate", "successful editing result has no candidate text");
495
484
  }
496
485
  return request.mode === "in_place"
497
486
  ? replaceDocumentFile(request.target, candidateText, {
@@ -515,9 +504,9 @@ function renderWriteSummary(operation, result) {
515
504
  return `WRITE ${operation} mode=${result.mode} target=${result.target} digest=${result.digest} written=${result.written}\n`;
516
505
  }
517
506
  async function runFormat(args) {
518
- const parsed = parseOptions(args, new Set(["format", "color", "max-diagnostics", "out", "expect-digest"]), new Set(["check", "diff", "write", "warnings-as-errors"]));
507
+ const parsed = parseCommandOptions("document.format", args);
519
508
  if (parsed.positionals.length !== 1) {
520
- throw new UsageError("dsl format requires exactly one <file>");
509
+ throw new UsageError("document format requires exactly one <file>");
521
510
  }
522
511
  const format = outputFormat(parsed.values.get("format"));
523
512
  const color = colorMode(parsed.values.get("color"), format);
@@ -530,7 +519,7 @@ async function runFormat(args) {
530
519
  input = await readDocument(sourceOperand);
531
520
  }
532
521
  catch (error) {
533
- return cliError(error instanceof Error ? error : new Error(String(error)), 3, "dsl.format", format === "json");
522
+ return cliError(error instanceof Error ? error : new Error(String(error)), 3, "document.format", format === "json");
534
523
  }
535
524
  const result = planFormat(input.text, {
536
525
  maxDiagnostics,
@@ -551,14 +540,15 @@ async function runFormat(args) {
551
540
  }
552
541
  }
553
542
  catch (error) {
554
- return writeFailureExit(error, "dsl.format", format === "json");
543
+ return writeFailureExit(error, "document.format", format === "json");
555
544
  }
556
545
  }
557
546
  if (format === "json") {
558
547
  writeJson({
559
548
  schema_version: "Perttool.FormatResult.v1",
549
+ cli_contract_version: 3,
560
550
  tool_version: TOOL_VERSION,
561
- operation: "dsl.format",
551
+ operation: "document.format",
562
552
  ok,
563
553
  document_id: result.documentId,
564
554
  source,
@@ -572,7 +562,7 @@ async function runFormat(args) {
572
562
  const candidateAllowed = result.ok && !warningFailure;
573
563
  if (candidateAllowed) {
574
564
  if (writeResult !== null) {
575
- process.stderr.write(renderWriteSummary("dsl.format", writeResult));
565
+ process.stderr.write(renderWriteSummary("document.format", writeResult));
576
566
  }
577
567
  else if (parsed.flags.has("check")) {
578
568
  if (parsed.flags.has("diff"))
@@ -581,7 +571,7 @@ async function runFormat(args) {
581
571
  else {
582
572
  process.stdout.write(parsed.flags.has("diff") ? (result.diff ?? "") : (result.updatedText ?? ""));
583
573
  if (!parsed.flags.has("diff")) {
584
- process.stderr.write(`PREVIEW dsl.format changed=${result.changed} original_digest=${result.originalDigest} updated_digest=${result.updatedDigest}\n`);
574
+ process.stderr.write(`PREVIEW document.format changed=${result.changed} original_digest=${result.originalDigest} updated_digest=${result.updatedDigest}\n`);
585
575
  }
586
576
  }
587
577
  }
@@ -594,46 +584,6 @@ async function runFormat(args) {
594
584
  }
595
585
  return ok ? 0 : 1;
596
586
  }
597
- function mutationOptionSets(resource, action) {
598
- const values = new Set(mutationCommonValueOptions);
599
- const flags = new Set(mutationCommonFlagOptions);
600
- const repeatable = new Set();
601
- const addValues = (...names) => {
602
- for (const name of names)
603
- values.add(name);
604
- };
605
- const addRepeatable = (...names) => {
606
- for (const name of names)
607
- repeatable.add(name);
608
- };
609
- if (resource === "task" && action === "add") {
610
- addValues("title", "description", "duration", "optimistic", "most-likely", "pessimistic", "status", "priority", "owner", "blocked-reason", "source");
611
- addRepeatable("tag", "require");
612
- }
613
- else if (resource === "task" && action === "set") {
614
- addValues("from", "to", "title", "description", "duration", "optimistic", "most-likely", "pessimistic", "status", "priority", "owner", "blocked-reason", "source");
615
- addRepeatable("require", "add-tag", "remove-tag", "remove-require", "clear");
616
- }
617
- else if (resource === "milestone" && action === "add") {
618
- addValues("title", "description", "state");
619
- addRepeatable("tag");
620
- }
621
- else if (resource === "milestone" && action === "set") {
622
- addValues("title", "description", "state");
623
- addRepeatable("add-tag", "remove-tag", "clear");
624
- }
625
- else if (resource === "resource" && action === "add") {
626
- addValues("title", "description", "capacity");
627
- }
628
- else if (resource === "resource" && action === "set") {
629
- addValues("title", "description", "capacity");
630
- addRepeatable("clear");
631
- }
632
- else if (resource === "mutation" && action === "apply") {
633
- addValues("request");
634
- }
635
- return { values, flags, repeatable };
636
- }
637
587
  function requiredOption(parsed, name) {
638
588
  const value = parsed.values.get(name);
639
589
  if (value === undefined)
@@ -824,6 +774,62 @@ function taskMutationFromOptions(action, parsed) {
824
774
  ...(removeRequirements.length === 0 ? {} : { removeRequirements }),
825
775
  };
826
776
  }
777
+ function projectMutationFromOptions(parsed) {
778
+ if (parsed.positionals.length !== 1) {
779
+ throw new UsageError("project set requires exactly one <file>");
780
+ }
781
+ const version = optionalInteger(parsed, "version", 0, 2_147_483_647);
782
+ const durationUnit = enumOption(parsed.values.get("duration-unit"), "duration-unit", new Set(["day", "hour", "point"]));
783
+ const clear = enumRepeated(parsed, "clear", new Set([
784
+ "description",
785
+ "as_of",
786
+ "velocity",
787
+ "critical_epsilon",
788
+ "target_duration",
789
+ ]));
790
+ const conflicts = new Map([
791
+ ["description", parsed.values.has("description")],
792
+ ["as_of", parsed.values.has("as-of")],
793
+ ["velocity", parsed.values.has("velocity")],
794
+ ["critical_epsilon", parsed.values.has("critical-epsilon")],
795
+ ["target_duration", parsed.values.has("target-duration")],
796
+ ]);
797
+ const conflict = clear.find((field) => conflicts.get(field) === true);
798
+ if (conflict !== undefined) {
799
+ throw new UsageError(`--clear ${conflict} conflicts with another project field option`);
800
+ }
801
+ const set = {
802
+ ...(parsed.values.get("id") === undefined ? {} : { id: parsed.values.get("id") }),
803
+ ...(version === undefined ? {} : { version }),
804
+ ...(parsed.values.get("title") === undefined
805
+ ? {}
806
+ : { title: parsed.values.get("title") }),
807
+ ...(parsed.values.get("description") === undefined
808
+ ? {}
809
+ : { description: parsed.values.get("description") }),
810
+ ...(parsed.values.get("as-of") === undefined
811
+ ? {}
812
+ : { asOf: parsed.values.get("as-of") }),
813
+ ...(durationUnit === undefined ? {} : { durationUnit }),
814
+ ...(parsed.values.get("velocity") === undefined
815
+ ? {}
816
+ : { velocity: parsed.values.get("velocity") }),
817
+ ...(parsed.values.get("finish") === undefined
818
+ ? {}
819
+ : { finish: parsed.values.get("finish") }),
820
+ ...(parsed.values.get("critical-epsilon") === undefined
821
+ ? {}
822
+ : { criticalEpsilon: parsed.values.get("critical-epsilon") }),
823
+ ...(parsed.values.get("target-duration") === undefined
824
+ ? {}
825
+ : { targetDuration: parsed.values.get("target-duration") }),
826
+ };
827
+ return {
828
+ kind: "project.set",
829
+ ...(Object.keys(set).length === 0 ? {} : { set }),
830
+ ...(clear.length === 0 ? {} : { clear }),
831
+ };
832
+ }
827
833
  function milestoneMutationFromOptions(action, parsed) {
828
834
  if (parsed.positionals.length !== 2) {
829
835
  throw new UsageError(`milestone ${action} requires <file> <id>`);
@@ -918,6 +924,37 @@ function resourceMutationFromOptions(action, parsed) {
918
924
  ...(clear.length === 0 ? {} : { clear }),
919
925
  };
920
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
+ }
921
958
  function previewResultJson(result, exposeCandidate, writeRequest = { mode: "preview", target: null }, writeResult = null) {
922
959
  return {
923
960
  changed: exposeCandidate ? result.changed : false,
@@ -948,8 +985,7 @@ async function readMutationRequest(source) {
948
985
  }
949
986
  }
950
987
  async function runMutation(resource, action, args) {
951
- const config = mutationOptionSets(resource, action);
952
- const parsed = parseOptions(args, config.values, config.flags, config.repeatable);
988
+ const parsed = parseCommandOptions(`${resource}.${action}`, args);
953
989
  const format = outputFormat(parsed.values.get("format"));
954
990
  const color = colorMode(parsed.values.get("color"), format);
955
991
  const maxDiagnostics = boundedInteger(parsed.values.get("max-diagnostics"), "max-diagnostics", 100, 1, 1000);
@@ -957,9 +993,9 @@ async function runMutation(resource, action, args) {
957
993
  let sourceOperand;
958
994
  let writeRequest;
959
995
  let mutation;
960
- if (resource === "mutation") {
996
+ if (resource === "batch") {
961
997
  if (parsed.positionals.length !== 1) {
962
- throw new UsageError("mutation apply requires exactly one <file>");
998
+ throw new UsageError("batch apply requires exactly one <file>");
963
999
  }
964
1000
  sourceOperand = parsed.positionals[0];
965
1001
  writeRequest = editingWriteRequest(parsed, sourceOperand);
@@ -980,11 +1016,15 @@ async function runMutation(resource, action, args) {
980
1016
  }
981
1017
  else {
982
1018
  mutation =
983
- resource === "task"
984
- ? taskMutationFromOptions(action, parsed)
985
- : resource === "milestone"
986
- ? milestoneMutationFromOptions(action, parsed)
987
- : resourceMutationFromOptions(action, parsed);
1019
+ resource === "project"
1020
+ ? projectMutationFromOptions(parsed)
1021
+ : resource === "task"
1022
+ ? taskMutationFromOptions(action, parsed)
1023
+ : resource === "gate"
1024
+ ? gateMutationFromOptions(action, parsed)
1025
+ : resource === "milestone"
1026
+ ? milestoneMutationFromOptions(action, parsed)
1027
+ : resourceMutationFromOptions(action, parsed);
988
1028
  sourceOperand = parsed.positionals[0];
989
1029
  writeRequest = editingWriteRequest(parsed, sourceOperand);
990
1030
  }
@@ -1001,7 +1041,7 @@ async function runMutation(resource, action, args) {
1001
1041
  originalLabel: source,
1002
1042
  updatedLabel: "candidate",
1003
1043
  };
1004
- const result = resource === "mutation"
1044
+ const result = resource === "batch"
1005
1045
  ? planBatchMutation(input.text, mutation, mutationOptions)
1006
1046
  : planMutation(input.text, mutation, mutationOptions);
1007
1047
  const warningFailure = parsed.flags.has("warnings-as-errors") &&
@@ -1023,6 +1063,7 @@ async function runMutation(resource, action, args) {
1023
1063
  if (format === "json") {
1024
1064
  writeJson({
1025
1065
  schema_version: "Perttool.MutationResult.v1",
1066
+ cli_contract_version: 3,
1026
1067
  tool_version: TOOL_VERSION,
1027
1068
  operation,
1028
1069
  ok,
@@ -1077,7 +1118,7 @@ function renderAdvanceSummary(details) {
1077
1118
  ].join("\n");
1078
1119
  }
1079
1120
  async function runAdvance(args) {
1080
- const parsed = parseOptions(args, new Set(["format", "color", "max-diagnostics", "out", "expect-digest"]), new Set(["diff", "write", "warnings-as-errors"]));
1121
+ const parsed = parseCommandOptions("dag.advance", args);
1081
1122
  if (parsed.positionals.length !== 1) {
1082
1123
  throw new UsageError("dag advance requires exactly one <file>");
1083
1124
  }
@@ -1118,6 +1159,7 @@ async function runAdvance(args) {
1118
1159
  if (format === "json") {
1119
1160
  writeJson({
1120
1161
  schema_version: "Perttool.MutationResult.v1",
1162
+ cli_contract_version: 3,
1121
1163
  tool_version: TOOL_VERSION,
1122
1164
  operation: "dag.advance",
1123
1165
  ok,
@@ -1398,14 +1440,7 @@ function renderAnalysisText(result) {
1398
1440
  return `${lines.join("\n")}\n`;
1399
1441
  }
1400
1442
  async function runAnalyze(args) {
1401
- const parsed = parseOptions(args, new Set([
1402
- "schedule",
1403
- "max-paths",
1404
- "precision",
1405
- "max-diagnostics",
1406
- "format",
1407
- "color",
1408
- ]), new Set(["warnings-as-errors"]), new Set(["capacity"]));
1443
+ const parsed = parseCommandOptions("dag.analyze", args);
1409
1444
  if (parsed.positionals.length !== 1) {
1410
1445
  throw new UsageError("dag analyze requires exactly one <file>");
1411
1446
  }
@@ -1439,6 +1474,7 @@ async function runAnalyze(args) {
1439
1474
  if (format === "json") {
1440
1475
  writeJson({
1441
1476
  schema_version: "Perttool.AnalysisResult.v2",
1477
+ cli_contract_version: 3,
1442
1478
  tool_version: TOOL_VERSION,
1443
1479
  operation: "dag.analyze",
1444
1480
  ok,
@@ -1498,15 +1534,7 @@ function renderConversionLoss(loss, source, color) {
1498
1534
  }, source, color);
1499
1535
  }
1500
1536
  async function runRender(args) {
1501
- const parsed = parseOptions(args, new Set([
1502
- "to",
1503
- "profile",
1504
- "analysis",
1505
- "out",
1506
- "max-diagnostics",
1507
- "format",
1508
- "color",
1509
- ]), new Set(["strict-loss", "warnings-as-errors"]), new Set(["capacity"]));
1537
+ const parsed = parseCommandOptions("dag.render", args);
1510
1538
  if (parsed.positionals.length !== 1) {
1511
1539
  throw new UsageError("dag render requires exactly one <file>");
1512
1540
  }
@@ -1558,6 +1586,7 @@ async function runRender(args) {
1558
1586
  if (format === "json") {
1559
1587
  writeJson({
1560
1588
  schema_version: "Perttool.ExportResult.v1",
1589
+ cli_contract_version: 3,
1561
1590
  tool_version: TOOL_VERSION,
1562
1591
  operation: "dag.render",
1563
1592
  ok,
@@ -1608,7 +1637,7 @@ async function runRender(args) {
1608
1637
  return strictFailure ? 4 : ok ? 0 : 1;
1609
1638
  }
1610
1639
  async function runImport(args) {
1611
- const parsed = parseOptions(args, new Set(["from", "out", "max-diagnostics", "format", "color"]), new Set(["strict-loss", "warnings-as-errors"]));
1640
+ const parsed = parseCommandOptions("dag.import", args);
1612
1641
  if (parsed.positionals.length !== 1) {
1613
1642
  throw new UsageError("dag import requires exactly one <file>");
1614
1643
  }
@@ -1649,6 +1678,7 @@ async function runImport(args) {
1649
1678
  if (format === "json") {
1650
1679
  writeJson({
1651
1680
  schema_version: "Perttool.ImportResult.v1",
1681
+ cli_contract_version: 3,
1652
1682
  tool_version: TOOL_VERSION,
1653
1683
  operation: "dag.import",
1654
1684
  ok,
@@ -1907,7 +1937,7 @@ function renderNextText(result) {
1907
1937
  return `${lines.join("\n")}\n`;
1908
1938
  }
1909
1939
  async function runNext(args) {
1910
- const parsed = parseOptions(args, new Set(["explain-depth", "precision", "max-diagnostics", "format", "color"]), new Set(["warnings-as-errors"]), new Set(["capacity"]));
1940
+ const parsed = parseCommandOptions("dag.next", args);
1911
1941
  if (parsed.positionals.length !== 1) {
1912
1942
  throw new UsageError("dag next requires exactly one <file>");
1913
1943
  }
@@ -1938,6 +1968,7 @@ async function runNext(args) {
1938
1968
  if (format === "json") {
1939
1969
  writeJson({
1940
1970
  schema_version: "Perttool.CliError.v1",
1971
+ cli_contract_version: 3,
1941
1972
  tool_version: TOOL_VERSION,
1942
1973
  operation: "dag.next",
1943
1974
  ok: false,
@@ -1958,6 +1989,7 @@ async function runNext(args) {
1958
1989
  if (format === "json") {
1959
1990
  writeJson({
1960
1991
  schema_version: "Perttool.NextResult.v3",
1992
+ cli_contract_version: 3,
1961
1993
  recommendation_interface_version: 1,
1962
1994
  tool_version: TOOL_VERSION,
1963
1995
  operation: "dag.next",
@@ -1982,62 +2014,140 @@ async function runNext(args) {
1982
2014
  }
1983
2015
  return ok ? 0 : 1;
1984
2016
  }
1985
- function renderHelpText(result) {
1986
- const lines = [result.title, "", result.summary];
1987
- if (result.topics.length > 0) {
1988
- lines.push("", "Topics:");
1989
- for (const topic of result.topics)
1990
- lines.push(` ${topic.id.padEnd(12)} ${topic.summary}`);
2017
+ function runGuide(args) {
2018
+ const parsed = parseCommandOptions("guide", args);
2019
+ if (parsed.positionals.length > 2) {
2020
+ throw new UsageError("guide accepts at most <topic> <subtopic>");
2021
+ }
2022
+ const format = outputFormat(parsed.values.get("format"));
2023
+ colorMode(parsed.values.get("color"), format);
2024
+ const topicId = parsed.positionals.length === 0 ? null : parsed.positionals.join(".");
2025
+ const level = helpLevel(parsed.values.get("level"), topicId !== null);
2026
+ const result = getGuide(topicId, level);
2027
+ if (format === "json") {
2028
+ process.stdout.write(serializeGuideResult(result));
2029
+ }
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>");
1991
2045
  }
1992
- for (const section of result.sections) {
1993
- lines.push("", section.title, section.body);
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));
1994
2053
  }
1995
- if (result.syntax.length > 0)
1996
- lines.push("", "Syntax:", ...result.syntax.map((line) => ` ${line}`));
1997
- if (result.examples.length > 0) {
1998
- lines.push("", "Examples:", ...result.examples.map((example) => ` ${example.id}: ${example.text}`));
2054
+ else {
2055
+ const rendered = renderCommandHelpResult(result);
2056
+ if (result.ok) {
2057
+ process.stdout.write(rendered);
2058
+ }
2059
+ else {
2060
+ process.stderr.write(rendered);
2061
+ }
1999
2062
  }
2000
- if (result.related.length > 0)
2001
- lines.push("", `Related: ${result.related.join(", ")}`);
2002
- return `${lines.join("\n")}\n`;
2063
+ return result.ok ? 0 : 1;
2003
2064
  }
2004
- function runHelp(args) {
2005
- const parsed = parseOptions(args, new Set(["level", "format", "color"]), new Set());
2065
+ function runAgentHelp(args) {
2066
+ const parsed = parseCommandOptions("agent.help", args);
2006
2067
  if (parsed.positionals.length > 2) {
2007
- throw new UsageError("dsl help accepts at most <topic> <subtopic>");
2068
+ throw new UsageError("agent help accepts at most <provider> <surface>");
2008
2069
  }
2009
2070
  const format = outputFormat(parsed.values.get("format"));
2010
- colorMode(parsed.values.get("color"), format);
2011
- const topicId = parsed.positionals.length === 0 ? null : parsed.positionals.join(".");
2012
- const level = helpLevel(parsed.values.get("level"), topicId !== null);
2013
- const result = getHelp(topicId, level);
2071
+ const color = colorMode(parsed.values.get("color"), format);
2072
+ const providerId = parsed.positionals[0] ?? null;
2073
+ const surfaceId = parsed.positionals[1] ?? null;
2074
+ const level = helpLevel(parsed.values.get("level"), providerId !== null);
2075
+ const result = getAgentHelp({
2076
+ providerId,
2077
+ surfaceId,
2078
+ level,
2079
+ });
2014
2080
  if (format === "json") {
2081
+ const projected = agentGuidanceResultToJson(result);
2082
+ const { schema_version: schemaVersion, ...payload } = projected;
2015
2083
  writeJson({
2016
- schema_version: "Perttool.HelpResult.v1",
2017
- tool_version: TOOL_VERSION,
2018
- operation: "dsl.help",
2019
- ok: result.ok,
2020
- diagnostics: result.diagnostics.map(jsonDiagnostic),
2021
- topic_id: result.topicId,
2022
- level: result.level,
2023
- title: result.title,
2024
- summary: result.summary,
2025
- sections: result.sections,
2026
- syntax: result.syntax,
2027
- examples: result.examples,
2028
- related: result.related,
2029
- topics: result.topics,
2084
+ schema_version: schemaVersion,
2085
+ cli_contract_version: 3,
2086
+ ...payload,
2030
2087
  });
2031
2088
  }
2032
- else if (result.ok) {
2033
- process.stdout.write(renderHelpText(result));
2034
- }
2035
2089
  else {
2090
+ if (result.ok) {
2091
+ process.stdout.write(renderAgentGuidanceText(result));
2092
+ }
2036
2093
  for (const diagnostic of result.diagnostics) {
2037
- process.stderr.write(`${renderDiagnostic(diagnostic, "<help>", "never")}\n`);
2094
+ process.stderr.write(`${renderDiagnostic({
2095
+ code: diagnostic.code,
2096
+ severity: diagnostic.severity,
2097
+ message: diagnostic.message,
2098
+ data: {
2099
+ provider_id: diagnostic.providerId,
2100
+ surface_id: diagnostic.surfaceId,
2101
+ },
2102
+ }, "<agent-guidance>", color)}\n`);
2038
2103
  }
2039
2104
  }
2040
- return result.ok ? 0 : 1;
2105
+ return agentGuidanceExitCode(result.diagnostics);
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;
2041
2151
  }
2042
2152
  async function main(argv) {
2043
2153
  if (argv.length === 1 && argv[0] === "--version") {
@@ -2045,59 +2155,37 @@ async function main(argv) {
2045
2155
  return 0;
2046
2156
  }
2047
2157
  if (argv.length === 1 && argv[0] === "--help") {
2048
- process.stdout.write(`${topLevelHelp()}\n`);
2049
- return 0;
2158
+ return runCommandHelp([]);
2050
2159
  }
2051
- if (argv.length < 2)
2052
- throw new UsageError("expected <resource> <action>");
2053
- const resource = argv[0];
2054
- const action = argv[1];
2055
- const entityActions = new Map([
2056
- ["task", new Set(["add", "set", "remove", "finish"])],
2057
- ["milestone", new Set(["add", "set", "remove"])],
2058
- ["resource", new Set(["add", "set", "remove"])],
2059
- ["mutation", new Set(["apply"])],
2060
- ]);
2061
- const isMutationCommand = entityActions.get(resource)?.has(action) === true;
2062
- if (argv.length === 3 &&
2063
- argv[2] === "--help" &&
2064
- ((resource === "dsl" && ["check", "format", "help"].includes(action)) ||
2065
- (resource === "dag" && ["analyze", "next", "advance", "render", "import"].includes(action)) ||
2066
- isMutationCommand)) {
2067
- process.stdout.write(`${commandHelp(resource, action)}\n`);
2068
- return 0;
2069
- }
2070
- if (resource === "dag" && action === "analyze") {
2071
- return runAnalyze(argv.slice(2));
2072
- }
2073
- if (resource === "dag" && action === "next") {
2074
- return runNext(argv.slice(2));
2075
- }
2076
- if (resource === "dag" && action === "advance") {
2077
- return runAdvance(argv.slice(2));
2078
- }
2079
- if (resource === "dag" && action === "render") {
2080
- return runRender(argv.slice(2));
2081
- }
2082
- if (resource === "dag" && action === "import") {
2083
- return runImport(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;
2084
2169
  }
2085
- if (resource === "dsl" && action === "format") {
2086
- return runFormat(argv.slice(2));
2170
+ const descriptor = validation.descriptor;
2171
+ if (validation.helpAlias) {
2172
+ return runCommandHelp([...descriptor.path]);
2087
2173
  }
2088
- if (isMutationCommand) {
2089
- return runMutation(resource, action, argv.slice(2));
2174
+ try {
2175
+ return await dispatchCommand(descriptor, argv.slice(descriptor.path.length));
2090
2176
  }
2091
- if (resource !== "dsl" || (action !== "check" && action !== "help")) {
2092
- throw new UsageError(`unknown or not-yet-implemented command: ${resource} ${action}`);
2177
+ catch (error) {
2178
+ if (error instanceof UsageError) {
2179
+ return emitCommandUsage(handlerCommandUsageError(descriptor, error.message), jsonRequested(argv));
2180
+ }
2181
+ throw error;
2093
2182
  }
2094
- return action === "check" ? runCheck(argv.slice(2)) : runHelp(argv.slice(2));
2095
2183
  }
2096
2184
  const args = process.argv.slice(2);
2097
2185
  try {
2098
2186
  process.exitCode = await main(args);
2099
2187
  }
2100
2188
  catch (error) {
2101
- process.exitCode = cliError(error instanceof Error ? error : new Error(String(error)), error instanceof UsageError ? 2 : 70, args.length >= 2 ? `${args[0]}.${args[1]}` : null, jsonRequested(args));
2189
+ process.exitCode = cliError(error instanceof Error ? error : new Error(String(error)), 70, null, jsonRequested(args));
2102
2190
  }
2103
2191
  //# sourceMappingURL=cli.js.map