perttool 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -1
- package/README.md +60 -27
- package/dist/analysis/temporal-deadline.d.ts +59 -0
- package/dist/analysis/temporal-deadline.d.ts.map +1 -0
- package/dist/analysis/temporal-deadline.js +387 -0
- package/dist/analysis/temporal-deadline.js.map +1 -0
- package/dist/analysis/temporal-precedence.d.ts +45 -0
- package/dist/analysis/temporal-precedence.d.ts.map +1 -0
- package/dist/analysis/temporal-precedence.js +123 -0
- package/dist/analysis/temporal-precedence.js.map +1 -0
- package/dist/analysis/temporal-resource.d.ts +66 -0
- package/dist/analysis/temporal-resource.d.ts.map +1 -0
- package/dist/analysis/temporal-resource.js +292 -0
- package/dist/analysis/temporal-resource.js.map +1 -0
- package/dist/application/check.d.ts +16 -0
- package/dist/application/check.d.ts.map +1 -1
- package/dist/application/check.js +51 -0
- package/dist/application/check.js.map +1 -1
- package/dist/application/contract4.d.ts +17 -0
- package/dist/application/contract4.d.ts.map +1 -0
- package/dist/application/contract4.js +27 -0
- package/dist/application/contract4.js.map +1 -0
- package/dist/application/init.d.ts +2 -1
- package/dist/application/init.d.ts.map +1 -1
- package/dist/application/init.js +11 -2
- package/dist/application/init.js.map +1 -1
- package/dist/application/mutate.d.ts +19 -0
- package/dist/application/mutate.d.ts.map +1 -1
- package/dist/application/mutate.js +34 -16
- package/dist/application/mutate.js.map +1 -1
- package/dist/application/next.d.ts +2 -0
- package/dist/application/next.d.ts.map +1 -1
- package/dist/application/next.js +7 -1
- package/dist/application/next.js.map +1 -1
- package/dist/application/project.d.ts +3 -1
- package/dist/application/project.d.ts.map +1 -1
- package/dist/application/project.js +2 -1
- package/dist/application/project.js.map +1 -1
- package/dist/application/target-check.d.ts +34 -0
- package/dist/application/target-check.d.ts.map +1 -0
- package/dist/application/target-check.js +90 -0
- package/dist/application/target-check.js.map +1 -0
- package/dist/application/target-grammar-boundary.d.ts +22 -0
- package/dist/application/target-grammar-boundary.d.ts.map +1 -0
- package/dist/application/target-grammar-boundary.js +91 -0
- package/dist/application/target-grammar-boundary.js.map +1 -0
- package/dist/application/target-mutate.d.ts +8 -0
- package/dist/application/target-mutate.d.ts.map +1 -0
- package/dist/application/target-mutate.js +56 -0
- package/dist/application/target-mutate.js.map +1 -0
- package/dist/application/target-project.d.ts +28 -0
- package/dist/application/target-project.d.ts.map +1 -0
- package/dist/application/target-project.js +60 -0
- package/dist/application/target-project.js.map +1 -0
- package/dist/application/target-temporal-analysis.d.ts +138 -0
- package/dist/application/target-temporal-analysis.d.ts.map +1 -0
- package/dist/application/target-temporal-analysis.js +409 -0
- package/dist/application/target-temporal-analysis.js.map +1 -0
- package/dist/application/target-temporal-input.d.ts +86 -0
- package/dist/application/target-temporal-input.d.ts.map +1 -0
- package/dist/application/target-temporal-input.js +238 -0
- package/dist/application/target-temporal-input.js.map +1 -0
- package/dist/application/target-unit-migration-candidate.d.ts +60 -0
- package/dist/application/target-unit-migration-candidate.d.ts.map +1 -0
- package/dist/application/target-unit-migration-candidate.js +290 -0
- package/dist/application/target-unit-migration-candidate.js.map +1 -0
- package/dist/application/target-unit-migration-request.d.ts +35 -0
- package/dist/application/target-unit-migration-request.d.ts.map +1 -0
- package/dist/application/target-unit-migration-request.js +68 -0
- package/dist/application/target-unit-migration-request.js.map +1 -0
- package/dist/application/target-unit-migration-result.d.ts +66 -0
- package/dist/application/target-unit-migration-result.d.ts.map +1 -0
- package/dist/application/target-unit-migration-result.js +86 -0
- package/dist/application/target-unit-migration-result.js.map +1 -0
- package/dist/application/unit-migration.d.ts +13 -0
- package/dist/application/unit-migration.d.ts.map +1 -0
- package/dist/application/unit-migration.js +9 -0
- package/dist/application/unit-migration.js.map +1 -0
- package/dist/cli.js +264 -26
- package/dist/cli.js.map +1 -1
- package/dist/command/discovery.d.ts +3 -3
- package/dist/command/discovery.d.ts.map +1 -1
- package/dist/command/discovery.js +149 -22
- package/dist/command/discovery.js.map +1 -1
- package/dist/command/registry.d.ts +1 -1
- package/dist/command/registry.d.ts.map +1 -1
- package/dist/command/registry.js +14 -1
- package/dist/command/registry.js.map +1 -1
- package/dist/command/usage.js +3 -3
- package/dist/formatter/source-formatter.d.ts +12 -0
- package/dist/formatter/source-formatter.d.ts.map +1 -1
- package/dist/formatter/source-formatter.js +48 -6
- package/dist/formatter/source-formatter.js.map +1 -1
- package/dist/formatter/target-source-formatter.d.ts +8 -0
- package/dist/formatter/target-source-formatter.d.ts.map +1 -0
- package/dist/formatter/target-source-formatter.js +36 -0
- package/dist/formatter/target-source-formatter.js.map +1 -0
- package/dist/help/guide.d.ts +1 -1
- package/dist/help/guide.js +1 -1
- package/dist/help/registry.d.ts.map +1 -1
- package/dist/help/registry.js +124 -13
- package/dist/help/registry.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/io/safe-write.d.ts +7 -0
- package/dist/io/safe-write.d.ts.map +1 -1
- package/dist/io/safe-write.js +19 -10
- package/dist/io/safe-write.js.map +1 -1
- package/dist/io/target-safe-write.d.ts +7 -0
- package/dist/io/target-safe-write.d.ts.map +1 -0
- package/dist/io/target-safe-write.js +33 -0
- package/dist/io/target-safe-write.js.map +1 -0
- package/dist/migration/conversion.d.ts +40 -0
- package/dist/migration/conversion.d.ts.map +1 -0
- package/dist/migration/conversion.js +106 -0
- package/dist/migration/conversion.js.map +1 -0
- package/dist/migration/grammar-boundary.d.ts +20 -0
- package/dist/migration/grammar-boundary.d.ts.map +1 -0
- package/dist/migration/grammar-boundary.js +92 -0
- package/dist/migration/grammar-boundary.js.map +1 -0
- package/dist/migration/request.d.ts +79 -0
- package/dist/migration/request.d.ts.map +1 -0
- package/dist/migration/request.js +308 -0
- package/dist/migration/request.js.map +1 -0
- package/dist/model/calendar-arithmetic.d.ts +46 -0
- package/dist/model/calendar-arithmetic.d.ts.map +1 -0
- package/dist/model/calendar-arithmetic.js +236 -0
- package/dist/model/calendar-arithmetic.js.map +1 -0
- package/dist/model/calendar.d.ts +22 -0
- package/dist/model/calendar.d.ts.map +1 -0
- package/dist/model/calendar.js +83 -0
- package/dist/model/calendar.js.map +1 -0
- package/dist/model/declaration-fields.d.ts +15 -0
- package/dist/model/declaration-fields.d.ts.map +1 -0
- package/dist/model/declaration-fields.js +54 -0
- package/dist/model/declaration-fields.js.map +1 -0
- package/dist/model/exact-duration-source.d.ts +10 -0
- package/dist/model/exact-duration-source.d.ts.map +1 -0
- package/dist/model/exact-duration-source.js +69 -0
- package/dist/model/exact-duration-source.js.map +1 -0
- package/dist/model/rational.d.ts +2 -2
- package/dist/model/rational.d.ts.map +1 -1
- package/dist/model/rational.js +3 -1
- package/dist/model/rational.js.map +1 -1
- package/dist/model/syntax.d.ts +9 -2
- package/dist/model/syntax.d.ts.map +1 -1
- package/dist/model/syntax.js +20 -4
- package/dist/model/syntax.js.map +1 -1
- package/dist/model/target-calendar.d.ts +25 -0
- package/dist/model/target-calendar.d.ts.map +1 -0
- package/dist/model/target-calendar.js +56 -0
- package/dist/model/target-calendar.js.map +1 -0
- package/dist/mutation/advance.d.ts.map +1 -1
- package/dist/mutation/advance.js +21 -4
- package/dist/mutation/advance.js.map +1 -1
- package/dist/mutation/milestone.d.ts +10 -1
- package/dist/mutation/milestone.d.ts.map +1 -1
- package/dist/mutation/milestone.js +77 -21
- package/dist/mutation/milestone.js.map +1 -1
- package/dist/mutation/project.d.ts +7 -1
- package/dist/mutation/project.d.ts.map +1 -1
- package/dist/mutation/project.js +18 -5
- package/dist/mutation/project.js.map +1 -1
- package/dist/mutation/target-types.d.ts +42 -0
- package/dist/mutation/target-types.d.ts.map +1 -0
- package/dist/mutation/target-types.js +2 -0
- package/dist/mutation/target-types.js.map +1 -0
- package/dist/mutation/task.d.ts +12 -1
- package/dist/mutation/task.d.ts.map +1 -1
- package/dist/mutation/task.js +108 -49
- package/dist/mutation/task.js.map +1 -1
- package/dist/mutation/types.d.ts +8 -2
- package/dist/mutation/types.d.ts.map +1 -1
- package/dist/parser/document-parser.d.ts +14 -0
- package/dist/parser/document-parser.d.ts.map +1 -1
- package/dist/parser/document-parser.js +142 -38
- package/dist/parser/document-parser.js.map +1 -1
- package/dist/recommendation/override-types.d.ts +2 -2
- package/dist/recommendation/override.d.ts +2 -2
- package/dist/recommendation/override.d.ts.map +1 -1
- package/dist/recommendation/override.js +49 -12
- package/dist/recommendation/override.js.map +1 -1
- package/dist/semantic/target-validator.d.ts +36 -0
- package/dist/semantic/target-validator.d.ts.map +1 -0
- package/dist/semantic/target-validator.js +87 -0
- package/dist/semantic/target-validator.js.map +1 -0
- package/dist/semantic/validator.d.ts +2 -0
- package/dist/semantic/validator.d.ts.map +1 -1
- package/dist/semantic/validator.js +50 -5
- package/dist/semantic/validator.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { TextDecoder } from "node:util";
|
|
5
|
-
import { analyzeDocument } from "./application/
|
|
5
|
+
import { analyzeDocument, selectNextTasks, } from "./application/contract4.js";
|
|
6
6
|
import { checkDocument } from "./application/check.js";
|
|
7
7
|
import { planFormat } from "./application/format.js";
|
|
8
8
|
import { planProjectInit, projectInitResultToJson, renderProjectInitResult, withProjectInitOutput, } from "./application/init.js";
|
|
9
9
|
import { planBatchMutation, planMutation } from "./application/mutate.js";
|
|
10
|
-
import {
|
|
10
|
+
import { planUnitMigration, withUnitMigrationWrite, } from "./application/unit-migration.js";
|
|
11
11
|
import { getProjectMetadata, } from "./application/project.js";
|
|
12
12
|
import { getAgentHelp } from "./application/agent-help.js";
|
|
13
13
|
import { exportMermaid, } from "./conversion/mermaid.js";
|
|
14
14
|
import { importMermaid } from "./conversion/mermaid-import.js";
|
|
15
15
|
import { getGuide, renderGuideResult, serializeGuideResult, } from "./help/guide.js";
|
|
16
16
|
import { commandOptionSets, } from "./command/registry.js";
|
|
17
|
-
import {
|
|
17
|
+
import { CONTRACT4_COMMAND_REGISTRY, getCommandDiscovery, renderCommandHelpResult, serializeCommandHelpResult, } from "./command/discovery.js";
|
|
18
18
|
import { handlerCommandUsageError, renderCommandUsageError, serializeCommandUsageError, validateCommandInvocation, } from "./command/usage.js";
|
|
19
19
|
import { agentGuidanceResultToJson } from "./guidance/projection.js";
|
|
20
20
|
import { agentGuidanceExitCode, renderAgentGuidanceText, } from "./guidance/text.js";
|
|
@@ -79,7 +79,7 @@ function parseOptions(args, valueOptions, flagOptions, repeatableOptions = new S
|
|
|
79
79
|
return { positionals, values, repeatedValues, flags };
|
|
80
80
|
}
|
|
81
81
|
function parseCommandOptions(operation, args) {
|
|
82
|
-
const descriptor =
|
|
82
|
+
const descriptor = CONTRACT4_COMMAND_REGISTRY.find((candidate) => candidate.operation === operation);
|
|
83
83
|
if (descriptor === undefined) {
|
|
84
84
|
throw new Error(`command descriptor is missing for ${operation}`);
|
|
85
85
|
}
|
|
@@ -182,6 +182,18 @@ async function readStdin() {
|
|
|
182
182
|
function writeJson(value) {
|
|
183
183
|
process.stdout.write(`${JSON.stringify(value)}\n`);
|
|
184
184
|
}
|
|
185
|
+
function snakeJson(value) {
|
|
186
|
+
if (typeof value === "bigint")
|
|
187
|
+
return value.toString();
|
|
188
|
+
if (Array.isArray(value))
|
|
189
|
+
return value.map(snakeJson);
|
|
190
|
+
if (value === null || typeof value !== "object")
|
|
191
|
+
return value;
|
|
192
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
193
|
+
key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase(),
|
|
194
|
+
snakeJson(item),
|
|
195
|
+
]));
|
|
196
|
+
}
|
|
185
197
|
function cliError(error, exitCode, operation, json) {
|
|
186
198
|
const code = error instanceof UsageError
|
|
187
199
|
? error.code
|
|
@@ -205,7 +217,7 @@ function cliError(error, exitCode, operation, json) {
|
|
|
205
217
|
if (json) {
|
|
206
218
|
writeJson({
|
|
207
219
|
schema_version: "Perttool.CliError.v1",
|
|
208
|
-
cli_contract_version:
|
|
220
|
+
cli_contract_version: 4,
|
|
209
221
|
tool_version: TOOL_VERSION,
|
|
210
222
|
operation,
|
|
211
223
|
ok: false,
|
|
@@ -240,8 +252,8 @@ async function runCheck(args) {
|
|
|
240
252
|
const ok = result.ok && !warningFailure;
|
|
241
253
|
if (format === "json") {
|
|
242
254
|
writeJson({
|
|
243
|
-
schema_version: "Perttool.CheckResult.
|
|
244
|
-
cli_contract_version:
|
|
255
|
+
schema_version: "Perttool.CheckResult.v2",
|
|
256
|
+
cli_contract_version: 4,
|
|
245
257
|
tool_version: TOOL_VERSION,
|
|
246
258
|
operation: "document.check",
|
|
247
259
|
ok,
|
|
@@ -252,11 +264,12 @@ async function runCheck(args) {
|
|
|
252
264
|
diagnostics_truncated: result.diagnosticsTruncated,
|
|
253
265
|
grammar_version: result.grammarVersion,
|
|
254
266
|
summary: result.summary,
|
|
267
|
+
temporal_inputs: snakeJson(result.temporalInputs),
|
|
255
268
|
});
|
|
256
269
|
}
|
|
257
270
|
else {
|
|
258
271
|
if (ok) {
|
|
259
|
-
process.stdout.write(`OK ${source} project=${result.documentId ?? "-"} milestones=${result.summary.milestones} tasks=${result.summary.tasks} gates=${result.summary.gates} resources=${result.summary.resources}\n`);
|
|
272
|
+
process.stdout.write(`OK ${source} project=${result.documentId ?? "-"} milestones=${result.summary.milestones} tasks=${result.summary.tasks} gates=${result.summary.gates} resources=${result.summary.resources} temporal=milestone_deadlines:${result.temporalInputs?.milestoneDeadlines.length ?? 0},task_not_before:${result.temporalInputs?.taskConstraints.filter(({ notBefore }) => notBefore !== null).length ?? 0},task_deadlines:${result.temporalInputs?.taskConstraints.filter(({ deadline }) => deadline !== null).length ?? 0}\n`);
|
|
260
273
|
}
|
|
261
274
|
for (const diagnostic of result.diagnostics) {
|
|
262
275
|
process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
|
|
@@ -273,25 +286,30 @@ function projectJson(project) {
|
|
|
273
286
|
version: project.version,
|
|
274
287
|
title: project.title,
|
|
275
288
|
description: project.description,
|
|
276
|
-
as_of: project.asOf,
|
|
289
|
+
as_of: snakeJson(project.asOf),
|
|
277
290
|
duration_unit: project.durationUnit,
|
|
278
291
|
velocity: project.velocity,
|
|
279
292
|
finish: project.finish,
|
|
293
|
+
finish_deadline: snakeJson(project.finishDeadline),
|
|
280
294
|
critical_epsilon: project.criticalEpsilon,
|
|
281
295
|
target_duration: project.targetDuration,
|
|
282
296
|
};
|
|
283
297
|
}
|
|
284
298
|
function renderProjectText(project) {
|
|
285
299
|
const optional = (value) => value ?? "-";
|
|
300
|
+
const calendar = (value) => value === null
|
|
301
|
+
? "-"
|
|
302
|
+
: `${value.kind.toUpperCase()} ${value.sourceText ?? "-"}`;
|
|
286
303
|
return [
|
|
287
304
|
`PROJECT ${project.id}`,
|
|
288
305
|
`VERSION ${project.version}`,
|
|
289
306
|
`TITLE ${JSON.stringify(project.title)}`,
|
|
290
307
|
`DESCRIPTION ${project.description === null ? "-" : JSON.stringify(project.description)}`,
|
|
291
|
-
`AS_OF ${
|
|
308
|
+
`AS_OF ${calendar(project.asOf)}`,
|
|
292
309
|
`DURATION_UNIT ${project.durationUnit}`,
|
|
293
310
|
`VELOCITY ${optional(project.velocity)}`,
|
|
294
311
|
`FINISH ${project.finish}`,
|
|
312
|
+
`FINISH_DEADLINE ${calendar(project.finishDeadline)}`,
|
|
295
313
|
`CRITICAL_EPSILON ${optional(project.criticalEpsilon)}`,
|
|
296
314
|
`TARGET_DURATION ${optional(project.targetDuration)}`,
|
|
297
315
|
"",
|
|
@@ -321,8 +339,8 @@ async function runProjectShow(args) {
|
|
|
321
339
|
const ok = result.ok && !warningFailure;
|
|
322
340
|
if (format === "json") {
|
|
323
341
|
writeJson({
|
|
324
|
-
schema_version: "Perttool.ProjectResult.
|
|
325
|
-
cli_contract_version:
|
|
342
|
+
schema_version: "Perttool.ProjectResult.v2",
|
|
343
|
+
cli_contract_version: 4,
|
|
326
344
|
tool_version: TOOL_VERSION,
|
|
327
345
|
operation: "project.show",
|
|
328
346
|
ok,
|
|
@@ -368,6 +386,11 @@ async function runProjectInit(args) {
|
|
|
368
386
|
...(parsed.values.get("velocity") === undefined
|
|
369
387
|
? {}
|
|
370
388
|
: { velocity: parsed.values.get("velocity") }),
|
|
389
|
+
...(parsed.values.get("initial-milestone-deadline") === undefined
|
|
390
|
+
? {}
|
|
391
|
+
: {
|
|
392
|
+
initialMilestoneDeadline: parsed.values.get("initial-milestone-deadline"),
|
|
393
|
+
}),
|
|
371
394
|
});
|
|
372
395
|
let writeResult = null;
|
|
373
396
|
const output = parsed.values.get("out");
|
|
@@ -546,7 +569,7 @@ async function runFormat(args) {
|
|
|
546
569
|
if (format === "json") {
|
|
547
570
|
writeJson({
|
|
548
571
|
schema_version: "Perttool.FormatResult.v1",
|
|
549
|
-
cli_contract_version:
|
|
572
|
+
cli_contract_version: 4,
|
|
550
573
|
tool_version: TOOL_VERSION,
|
|
551
574
|
operation: "document.format",
|
|
552
575
|
ok,
|
|
@@ -700,6 +723,12 @@ function taskMutationFromOptions(action, parsed) {
|
|
|
700
723
|
...(parsed.values.get("description") === undefined
|
|
701
724
|
? {}
|
|
702
725
|
: { description: parsed.values.get("description") }),
|
|
726
|
+
...(parsed.values.get("not-before") === undefined
|
|
727
|
+
? {}
|
|
728
|
+
: { notBefore: parsed.values.get("not-before") }),
|
|
729
|
+
...(parsed.values.get("deadline") === undefined
|
|
730
|
+
? {}
|
|
731
|
+
: { deadline: parsed.values.get("deadline") }),
|
|
703
732
|
...(status === undefined ? {} : { status }),
|
|
704
733
|
...(priority === undefined ? {} : { priority }),
|
|
705
734
|
...(requirements.length === 0 ? {} : { requirements }),
|
|
@@ -720,7 +749,7 @@ function taskMutationFromOptions(action, parsed) {
|
|
|
720
749
|
}
|
|
721
750
|
const clear = enumRepeated(parsed, "clear", new Set([
|
|
722
751
|
"description", "status", "priority", "owner", "blocked_reason", "source",
|
|
723
|
-
"tags", "requires",
|
|
752
|
+
"tags", "requires", "not_before", "deadline",
|
|
724
753
|
]));
|
|
725
754
|
const addTags = uniqueRepeated(parsed, "add-tag");
|
|
726
755
|
const removeTags = uniqueRepeated(parsed, "remove-tag");
|
|
@@ -735,6 +764,12 @@ function taskMutationFromOptions(action, parsed) {
|
|
|
735
764
|
? {}
|
|
736
765
|
: { description: parsed.values.get("description") }),
|
|
737
766
|
...timing,
|
|
767
|
+
...(parsed.values.get("not-before") === undefined
|
|
768
|
+
? {}
|
|
769
|
+
: { notBefore: parsed.values.get("not-before") }),
|
|
770
|
+
...(parsed.values.get("deadline") === undefined
|
|
771
|
+
? {}
|
|
772
|
+
: { deadline: parsed.values.get("deadline") }),
|
|
738
773
|
...(status === undefined ? {} : { status }),
|
|
739
774
|
...(priority === undefined ? {} : { priority }),
|
|
740
775
|
...(parsed.values.get("owner") === undefined
|
|
@@ -749,6 +784,8 @@ function taskMutationFromOptions(action, parsed) {
|
|
|
749
784
|
};
|
|
750
785
|
const clearConflicts = new Map([
|
|
751
786
|
["description", parsed.values.has("description")],
|
|
787
|
+
["not_before", parsed.values.has("not-before")],
|
|
788
|
+
["deadline", parsed.values.has("deadline")],
|
|
752
789
|
["status", status !== undefined],
|
|
753
790
|
["priority", priority !== undefined],
|
|
754
791
|
["owner", parsed.values.has("owner")],
|
|
@@ -848,18 +885,22 @@ function milestoneMutationFromOptions(action, parsed) {
|
|
|
848
885
|
? {}
|
|
849
886
|
: { description: parsed.values.get("description") }),
|
|
850
887
|
...(state === undefined ? {} : { state }),
|
|
888
|
+
...(parsed.values.get("deadline") === undefined
|
|
889
|
+
? {}
|
|
890
|
+
: { deadline: parsed.values.get("deadline") }),
|
|
851
891
|
...((parsed.repeatedValues.get("tag") ?? []).length === 0
|
|
852
892
|
? {}
|
|
853
893
|
: { tags: parsed.repeatedValues.get("tag") }),
|
|
854
894
|
},
|
|
855
895
|
};
|
|
856
896
|
}
|
|
857
|
-
const clear = enumRepeated(parsed, "clear", new Set(["description", "state", "tags"]));
|
|
897
|
+
const clear = enumRepeated(parsed, "clear", new Set(["description", "state", "deadline", "tags"]));
|
|
858
898
|
const addTags = uniqueRepeated(parsed, "add-tag");
|
|
859
899
|
const removeTags = uniqueRepeated(parsed, "remove-tag");
|
|
860
900
|
ensureDisjoint(addTags, "add-tag", removeTags, "remove-tag");
|
|
861
901
|
if (clear.some((field) => (field === "description" && parsed.values.has("description")) ||
|
|
862
902
|
(field === "state" && state !== undefined) ||
|
|
903
|
+
(field === "deadline" && parsed.values.has("deadline")) ||
|
|
863
904
|
(field === "tags" && (addTags.length > 0 || removeTags.length > 0)))) {
|
|
864
905
|
throw new UsageError("--clear conflicts with another milestone field option");
|
|
865
906
|
}
|
|
@@ -871,6 +912,9 @@ function milestoneMutationFromOptions(action, parsed) {
|
|
|
871
912
|
? {}
|
|
872
913
|
: { description: parsed.values.get("description") }),
|
|
873
914
|
...(state === undefined ? {} : { state }),
|
|
915
|
+
...(parsed.values.get("deadline") === undefined
|
|
916
|
+
? {}
|
|
917
|
+
: { deadline: parsed.values.get("deadline") }),
|
|
874
918
|
};
|
|
875
919
|
return {
|
|
876
920
|
kind: "milestone.set",
|
|
@@ -1063,7 +1107,7 @@ async function runMutation(resource, action, args) {
|
|
|
1063
1107
|
if (format === "json") {
|
|
1064
1108
|
writeJson({
|
|
1065
1109
|
schema_version: "Perttool.MutationResult.v1",
|
|
1066
|
-
cli_contract_version:
|
|
1110
|
+
cli_contract_version: 4,
|
|
1067
1111
|
tool_version: TOOL_VERSION,
|
|
1068
1112
|
operation,
|
|
1069
1113
|
ok,
|
|
@@ -1096,6 +1140,142 @@ async function runMutation(resource, action, args) {
|
|
|
1096
1140
|
}
|
|
1097
1141
|
return ok ? 0 : 1;
|
|
1098
1142
|
}
|
|
1143
|
+
function unitMigrationJson(result, source, sourceDigest, ok) {
|
|
1144
|
+
const exact = (value) => ({
|
|
1145
|
+
numerator: value.numerator.toString(),
|
|
1146
|
+
denominator: value.denominator.toString(),
|
|
1147
|
+
unit: value.unit,
|
|
1148
|
+
});
|
|
1149
|
+
return {
|
|
1150
|
+
schema_version: result.schemaVersion,
|
|
1151
|
+
cli_contract_version: 4,
|
|
1152
|
+
tool_version: TOOL_VERSION,
|
|
1153
|
+
operation: "project.migrate-unit",
|
|
1154
|
+
ok,
|
|
1155
|
+
document_id: result.documentId,
|
|
1156
|
+
source,
|
|
1157
|
+
source_digest: sourceDigest,
|
|
1158
|
+
diagnostics: result.diagnostics.map(jsonDiagnostic),
|
|
1159
|
+
diagnostics_truncated: result.diagnosticsTruncated,
|
|
1160
|
+
unit_migration: result.unitMigration,
|
|
1161
|
+
source_grammar_version: result.sourceGrammarVersion,
|
|
1162
|
+
target_grammar_version: result.targetGrammarVersion,
|
|
1163
|
+
grammar_disposition: result.grammarDisposition,
|
|
1164
|
+
source_unit: result.sourceUnit,
|
|
1165
|
+
target_unit: result.targetUnit,
|
|
1166
|
+
effective_velocity: result.effectiveVelocity === null
|
|
1167
|
+
? null
|
|
1168
|
+
: {
|
|
1169
|
+
points: exact(result.effectiveVelocity.points),
|
|
1170
|
+
period: exact(result.effectiveVelocity.period),
|
|
1171
|
+
},
|
|
1172
|
+
velocity_disposition: result.velocityDisposition,
|
|
1173
|
+
changed: result.changed,
|
|
1174
|
+
converted_fields: result.convertedFields.map((field) => ({
|
|
1175
|
+
entity_kind: field.entityKind,
|
|
1176
|
+
entity_id: field.entityId,
|
|
1177
|
+
field_path: field.fieldPath,
|
|
1178
|
+
original: exact(field.original),
|
|
1179
|
+
converted: exact(field.converted),
|
|
1180
|
+
canonical_token: field.canonicalToken,
|
|
1181
|
+
})),
|
|
1182
|
+
reversibility: result.reversibility,
|
|
1183
|
+
qualifications: result.qualifications,
|
|
1184
|
+
unavailable_causes: result.unavailableCauses.map((cause) => ({
|
|
1185
|
+
cause: cause.cause,
|
|
1186
|
+
diagnostic_code: cause.diagnosticCode,
|
|
1187
|
+
field_paths: cause.fieldPaths,
|
|
1188
|
+
})),
|
|
1189
|
+
original_digest: result.originalDigest,
|
|
1190
|
+
updated_digest: result.updatedDigest,
|
|
1191
|
+
updated_text: result.updatedText,
|
|
1192
|
+
diff: result.diff,
|
|
1193
|
+
edits: result.edits.map((edit) => ({
|
|
1194
|
+
start_offset: edit.startOffset,
|
|
1195
|
+
end_offset: edit.endOffset,
|
|
1196
|
+
replacement: edit.replacement,
|
|
1197
|
+
})),
|
|
1198
|
+
write: {
|
|
1199
|
+
mode: result.write.mode,
|
|
1200
|
+
target: result.write.target,
|
|
1201
|
+
written: result.write.written,
|
|
1202
|
+
},
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
async function runUnitMigration(args) {
|
|
1206
|
+
const parsed = parseCommandOptions("project.migrate-unit", args);
|
|
1207
|
+
if (parsed.positionals.length !== 1) {
|
|
1208
|
+
throw new UsageError("project migrate-unit requires exactly one <file>");
|
|
1209
|
+
}
|
|
1210
|
+
const format = outputFormat(parsed.values.get("format"));
|
|
1211
|
+
const color = colorMode(parsed.values.get("color"), format);
|
|
1212
|
+
const targetUnit = enumOption(requiredOption(parsed, "to-unit"), "to-unit", new Set(["day", "hour", "point"]));
|
|
1213
|
+
const maxDiagnostics = boundedInteger(parsed.values.get("max-diagnostics"), "max-diagnostics", 100, 1, 1000);
|
|
1214
|
+
const sourceOperand = parsed.positionals[0];
|
|
1215
|
+
const writeRequest = editingWriteRequest(parsed, sourceOperand);
|
|
1216
|
+
const source = sourceOperand === "-" ? "<stdin>" : sourceOperand;
|
|
1217
|
+
let input;
|
|
1218
|
+
try {
|
|
1219
|
+
input = await readDocument(sourceOperand);
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
return cliError(error instanceof Error ? error : new Error(String(error)), 3, "project.migrate-unit", format === "json");
|
|
1223
|
+
}
|
|
1224
|
+
let result = planUnitMigration(input.text, {
|
|
1225
|
+
targetUnit,
|
|
1226
|
+
...(parsed.values.get("replacement-velocity") === undefined
|
|
1227
|
+
? {}
|
|
1228
|
+
: {
|
|
1229
|
+
replacementVelocity: parsed.values.get("replacement-velocity"),
|
|
1230
|
+
}),
|
|
1231
|
+
}, {
|
|
1232
|
+
maxDiagnostics,
|
|
1233
|
+
originalLabel: source,
|
|
1234
|
+
updatedLabel: "candidate",
|
|
1235
|
+
});
|
|
1236
|
+
const warningFailure = parsed.flags.has("warnings-as-errors") &&
|
|
1237
|
+
(result.diagnosticsTruncated ||
|
|
1238
|
+
result.diagnostics.some(({ severity }) => severity === "warning"));
|
|
1239
|
+
const ok = result.ok && !warningFailure;
|
|
1240
|
+
let writeResult = null;
|
|
1241
|
+
if (result.ok) {
|
|
1242
|
+
try {
|
|
1243
|
+
assertExpectedDigest(writeRequest, input.digest);
|
|
1244
|
+
if (ok && writeRequest.mode !== "preview") {
|
|
1245
|
+
writeResult = await commitCandidate(writeRequest, result.updatedText, input.digest);
|
|
1246
|
+
result = withUnitMigrationWrite(result, writeResult);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
catch (error) {
|
|
1250
|
+
return writeFailureExit(error, "project.migrate-unit", format === "json");
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
if (format === "json") {
|
|
1254
|
+
writeJson(unitMigrationJson(result, source, input.digest, ok));
|
|
1255
|
+
}
|
|
1256
|
+
else {
|
|
1257
|
+
if (ok) {
|
|
1258
|
+
if (writeResult !== null) {
|
|
1259
|
+
process.stderr.write(renderWriteSummary("project.migrate-unit", writeResult));
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
process.stdout.write(parsed.flags.has("diff")
|
|
1263
|
+
? (result.diff ?? "")
|
|
1264
|
+
: (result.updatedText ?? ""));
|
|
1265
|
+
if (!parsed.flags.has("diff")) {
|
|
1266
|
+
process.stderr.write(`PREVIEW project.migrate-unit changed=${result.changed} source_unit=${result.sourceUnit ?? "-"} target_unit=${result.targetUnit} original_digest=${result.originalDigest} updated_digest=${result.updatedDigest ?? "-"}\n`);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
for (const diagnostic of result.diagnostics) {
|
|
1271
|
+
process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
|
|
1272
|
+
}
|
|
1273
|
+
if (result.diagnosticsTruncated) {
|
|
1274
|
+
process.stderr.write(`DIAGNOSTICS_TRUNCATED true limit=${maxDiagnostics}\n`);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
return ok ? 0 : 1;
|
|
1278
|
+
}
|
|
1099
1279
|
function advanceResultJson(details) {
|
|
1100
1280
|
return details === null
|
|
1101
1281
|
? null
|
|
@@ -1159,7 +1339,7 @@ async function runAdvance(args) {
|
|
|
1159
1339
|
if (format === "json") {
|
|
1160
1340
|
writeJson({
|
|
1161
1341
|
schema_version: "Perttool.MutationResult.v1",
|
|
1162
|
-
cli_contract_version:
|
|
1342
|
+
cli_contract_version: 4,
|
|
1163
1343
|
tool_version: TOOL_VERSION,
|
|
1164
1344
|
operation: "dag.advance",
|
|
1165
1345
|
ok,
|
|
@@ -1437,6 +1617,41 @@ function renderAnalysisText(result) {
|
|
|
1437
1617
|
if (resource.resources.length === 0)
|
|
1438
1618
|
lines.push("-");
|
|
1439
1619
|
}
|
|
1620
|
+
if (result.temporal !== null) {
|
|
1621
|
+
const temporalPoint = (point) => {
|
|
1622
|
+
const causes = point.unavailableCauses
|
|
1623
|
+
.map(({ cause }) => cause)
|
|
1624
|
+
.join(",") || "-";
|
|
1625
|
+
return [
|
|
1626
|
+
`state=${point.state}`,
|
|
1627
|
+
`relative=${point.relative === null ? "-" : `${point.relative.numerator}/${point.relative.denominator}${durationSuffix(point.relative.unit)}`}`,
|
|
1628
|
+
`calendar=${point.calendar?.sourceText ?? "-"}`,
|
|
1629
|
+
`causes=${causes}`,
|
|
1630
|
+
].join(" ");
|
|
1631
|
+
};
|
|
1632
|
+
for (const [title, projection] of [
|
|
1633
|
+
["TEMPORAL PRECEDENCE", result.temporal.precedence],
|
|
1634
|
+
["TEMPORAL RESOURCE", result.temporal.resource],
|
|
1635
|
+
]) {
|
|
1636
|
+
lines.push("", title, `STATE ${projection.state}`, `ALGORITHM ${projection.algorithm === null ? "-" : `${projection.algorithm.id}@${projection.algorithm.version} optimal=${projection.algorithm.optimal}`}`, `CONDITIONAL_ON_BLOCKS_RESOLVED ${projection.conditionalOnBlocksResolved}`, `BLOCKED_TASKS ${projection.blockedTaskIds.join(",") || "-"}`);
|
|
1637
|
+
for (const task of projection.tasks) {
|
|
1638
|
+
lines.push(`${task.taskId} START ${temporalPoint(task.start)} FINISH ${temporalPoint(task.finish)}`);
|
|
1639
|
+
}
|
|
1640
|
+
if (projection.tasks.length === 0)
|
|
1641
|
+
lines.push("-");
|
|
1642
|
+
}
|
|
1643
|
+
lines.push("", "DEADLINES");
|
|
1644
|
+
for (const evaluation of result.temporal.deadlineEvaluations) {
|
|
1645
|
+
const causes = [
|
|
1646
|
+
...evaluation.current.unavailableCauses,
|
|
1647
|
+
...evaluation.precedence.unavailableCauses,
|
|
1648
|
+
...evaluation.resource.unavailableCauses,
|
|
1649
|
+
].map(({ cause }) => cause).join(",") || "-";
|
|
1650
|
+
lines.push(`${evaluation.subject.kind}:${evaluation.subject.id} assessment=${evaluation.combinedAssessment} precedence=${evaluation.precedence.assessment ?? evaluation.precedence.state} resource=${evaluation.resource.assessment ?? evaluation.resource.state} conditional=${evaluation.conditionalOnBlocksResolved} causes=${causes}`);
|
|
1651
|
+
}
|
|
1652
|
+
if (result.temporal.deadlineEvaluations.length === 0)
|
|
1653
|
+
lines.push("-");
|
|
1654
|
+
}
|
|
1440
1655
|
return `${lines.join("\n")}\n`;
|
|
1441
1656
|
}
|
|
1442
1657
|
async function runAnalyze(args) {
|
|
@@ -1473,8 +1688,8 @@ async function runAnalyze(args) {
|
|
|
1473
1688
|
const ok = result.ok && !warningFailure;
|
|
1474
1689
|
if (format === "json") {
|
|
1475
1690
|
writeJson({
|
|
1476
|
-
schema_version: "Perttool.AnalysisResult.
|
|
1477
|
-
cli_contract_version:
|
|
1691
|
+
schema_version: "Perttool.AnalysisResult.v3",
|
|
1692
|
+
cli_contract_version: 4,
|
|
1478
1693
|
tool_version: TOOL_VERSION,
|
|
1479
1694
|
operation: "dag.analyze",
|
|
1480
1695
|
ok,
|
|
@@ -1483,6 +1698,7 @@ async function runAnalyze(args) {
|
|
|
1483
1698
|
source_digest: input.digest,
|
|
1484
1699
|
diagnostics: result.diagnostics.map(jsonDiagnostic),
|
|
1485
1700
|
diagnostics_truncated: result.diagnosticsTruncated,
|
|
1701
|
+
grammar_version: result.grammarVersion,
|
|
1486
1702
|
mode: result.mode,
|
|
1487
1703
|
precision: result.precision,
|
|
1488
1704
|
...(result.durationUnit === null
|
|
@@ -1499,6 +1715,7 @@ async function runAnalyze(args) {
|
|
|
1499
1715
|
resource: result.resource === null || result.durationUnit === null
|
|
1500
1716
|
? null
|
|
1501
1717
|
: resourceJson(result.resource, result.durationUnit, result.precision),
|
|
1718
|
+
temporal: snakeJson(result.temporal),
|
|
1502
1719
|
});
|
|
1503
1720
|
}
|
|
1504
1721
|
else {
|
|
@@ -1586,7 +1803,7 @@ async function runRender(args) {
|
|
|
1586
1803
|
if (format === "json") {
|
|
1587
1804
|
writeJson({
|
|
1588
1805
|
schema_version: "Perttool.ExportResult.v1",
|
|
1589
|
-
cli_contract_version:
|
|
1806
|
+
cli_contract_version: 4,
|
|
1590
1807
|
tool_version: TOOL_VERSION,
|
|
1591
1808
|
operation: "dag.render",
|
|
1592
1809
|
ok,
|
|
@@ -1678,7 +1895,7 @@ async function runImport(args) {
|
|
|
1678
1895
|
if (format === "json") {
|
|
1679
1896
|
writeJson({
|
|
1680
1897
|
schema_version: "Perttool.ImportResult.v1",
|
|
1681
|
-
cli_contract_version:
|
|
1898
|
+
cli_contract_version: 4,
|
|
1682
1899
|
tool_version: TOOL_VERSION,
|
|
1683
1900
|
operation: "dag.import",
|
|
1684
1901
|
ok,
|
|
@@ -1895,7 +2112,15 @@ function renderExplanation(node, indent) {
|
|
|
1895
2112
|
function renderNextText(result) {
|
|
1896
2113
|
const unit = result.durationUnit;
|
|
1897
2114
|
const taskById = new Map(result.tasks.map((task) => [task.id, task]));
|
|
2115
|
+
const authority = result.temporal.authority;
|
|
1898
2116
|
const lines = [
|
|
2117
|
+
"START AUTHORITY",
|
|
2118
|
+
`POLICY ${authority.policy}`,
|
|
2119
|
+
`STARTABLE RECOMMENDED ${authority.startableRecommendedTaskIds.join(",") || "-"}`,
|
|
2120
|
+
`DELAYED RECOMMENDED ${authority.delayedRecommendedTaskIds.join(",") || "-"}`,
|
|
2121
|
+
`UNAVAILABLE RECOMMENDED ${authority.unavailableRecommendedTaskIds.join(",") || "-"}`,
|
|
2122
|
+
"DEADLINE FACTS INFORMATIONAL FOR RANKING v1",
|
|
2123
|
+
"",
|
|
1899
2124
|
`PERTTOOL NEXT ${result.documentId ?? "-"}`,
|
|
1900
2125
|
`VELOCITY ${result.velocity === null ? "-" : velocityText(result.velocity, result.precision)}`,
|
|
1901
2126
|
`VELOCITY_FORECAST_UNIT ${result.velocityForecast?.targetUnit ?? "-"}`,
|
|
@@ -1934,6 +2159,15 @@ function renderNextText(result) {
|
|
|
1934
2159
|
section("READY / WAITING RESOURCE", result.groups.ready.filter((id) => !result.groups.runnableNow.includes(id)), "rejection");
|
|
1935
2160
|
section("BLOCKED NOW", result.groups.blockedNow, "blocked");
|
|
1936
2161
|
section("UPCOMING", result.groups.upcoming, "explanation");
|
|
2162
|
+
lines.push("", "TEMPORAL CONTEXT");
|
|
2163
|
+
for (const temporal of result.temporal.tasks) {
|
|
2164
|
+
const causes = temporal.timeEligibility.unavailableCauses
|
|
2165
|
+
.map(({ cause }) => cause)
|
|
2166
|
+
.join(",") || "-";
|
|
2167
|
+
lines.push(`${temporal.taskId} eligibility=${temporal.timeEligibility.state} release=${temporal.timeEligibility.releaseBound === null ? "-" : `${temporal.timeEligibility.releaseBound.numerator}/${temporal.timeEligibility.releaseBound.denominator}${durationSuffix(temporal.timeEligibility.releaseBound.unit)}`} task_deadline=${temporal.taskDeadline?.sourceText ?? "-"} destination=${temporal.destinationMilestoneId} destination_deadline=${temporal.destinationDeadline?.sourceText ?? "-"} causes=${causes}`);
|
|
2168
|
+
}
|
|
2169
|
+
if (result.temporal.tasks.length === 0)
|
|
2170
|
+
lines.push("-");
|
|
1937
2171
|
return `${lines.join("\n")}\n`;
|
|
1938
2172
|
}
|
|
1939
2173
|
async function runNext(args) {
|
|
@@ -1968,7 +2202,7 @@ async function runNext(args) {
|
|
|
1968
2202
|
if (format === "json") {
|
|
1969
2203
|
writeJson({
|
|
1970
2204
|
schema_version: "Perttool.CliError.v1",
|
|
1971
|
-
cli_contract_version:
|
|
2205
|
+
cli_contract_version: 4,
|
|
1972
2206
|
tool_version: TOOL_VERSION,
|
|
1973
2207
|
operation: "dag.next",
|
|
1974
2208
|
ok: false,
|
|
@@ -1988,8 +2222,8 @@ async function runNext(args) {
|
|
|
1988
2222
|
const ok = result.ok && !warningFailure;
|
|
1989
2223
|
if (format === "json") {
|
|
1990
2224
|
writeJson({
|
|
1991
|
-
schema_version: "Perttool.NextResult.
|
|
1992
|
-
cli_contract_version:
|
|
2225
|
+
schema_version: "Perttool.NextResult.v4",
|
|
2226
|
+
cli_contract_version: 4,
|
|
1993
2227
|
recommendation_interface_version: 1,
|
|
1994
2228
|
tool_version: TOOL_VERSION,
|
|
1995
2229
|
operation: "dag.next",
|
|
@@ -1999,7 +2233,9 @@ async function runNext(args) {
|
|
|
1999
2233
|
source_digest: input.digest,
|
|
2000
2234
|
diagnostics: result.diagnostics.map(jsonDiagnostic),
|
|
2001
2235
|
diagnostics_truncated: result.diagnosticsTruncated,
|
|
2236
|
+
grammar_version: result.grammarVersion,
|
|
2002
2237
|
...(result.durationUnit === null ? {} : nextJson(result)),
|
|
2238
|
+
temporal: snakeJson(result.temporal),
|
|
2003
2239
|
});
|
|
2004
2240
|
}
|
|
2005
2241
|
else {
|
|
@@ -2082,7 +2318,7 @@ function runAgentHelp(args) {
|
|
|
2082
2318
|
const { schema_version: schemaVersion, ...payload } = projected;
|
|
2083
2319
|
writeJson({
|
|
2084
2320
|
schema_version: schemaVersion,
|
|
2085
|
-
cli_contract_version:
|
|
2321
|
+
cli_contract_version: 4,
|
|
2086
2322
|
...payload,
|
|
2087
2323
|
});
|
|
2088
2324
|
}
|
|
@@ -2120,6 +2356,8 @@ async function dispatchCommand(descriptor, args) {
|
|
|
2120
2356
|
return runProjectInit(args);
|
|
2121
2357
|
case "project.show":
|
|
2122
2358
|
return runProjectShow(args);
|
|
2359
|
+
case "project.migrate-unit":
|
|
2360
|
+
return runUnitMigration(args);
|
|
2123
2361
|
case "dag.analyze":
|
|
2124
2362
|
return runAnalyze(args);
|
|
2125
2363
|
case "dag.next":
|
|
@@ -2138,7 +2376,7 @@ async function dispatchCommand(descriptor, args) {
|
|
|
2138
2376
|
const [resource, action] = descriptor.path;
|
|
2139
2377
|
return runMutation(resource, action, args);
|
|
2140
2378
|
}
|
|
2141
|
-
throw new Error(`no Contract
|
|
2379
|
+
throw new Error(`no Contract 4 handler for ${descriptor.operation}`);
|
|
2142
2380
|
}
|
|
2143
2381
|
function emitCommandUsage(error, json) {
|
|
2144
2382
|
if (json) {
|