@tiangong-lca/cli 0.0.7 → 0.0.8

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 (52) hide show
  1. package/README.md +83 -2
  2. package/dist/src/cli.js +1327 -40
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/lib/dataset-author.js +100 -0
  5. package/dist/src/lib/dataset-author.js.map +1 -0
  6. package/dist/src/lib/dataset-bilingual.js +545 -0
  7. package/dist/src/lib/dataset-bilingual.js.map +1 -0
  8. package/dist/src/lib/dataset-contract.js +350 -0
  9. package/dist/src/lib/dataset-contract.js.map +1 -0
  10. package/dist/src/lib/dataset-evidence-search.js +636 -0
  11. package/dist/src/lib/dataset-evidence-search.js.map +1 -0
  12. package/dist/src/lib/dataset-import-lca.js +171 -0
  13. package/dist/src/lib/dataset-import-lca.js.map +1 -0
  14. package/dist/src/lib/dataset-remote-refresh.js +166 -0
  15. package/dist/src/lib/dataset-remote-refresh.js.map +1 -0
  16. package/dist/src/lib/dataset-remote-verify.js +543 -0
  17. package/dist/src/lib/dataset-remote-verify.js.map +1 -0
  18. package/dist/src/lib/dataset-validate.js +63 -7
  19. package/dist/src/lib/dataset-validate.js.map +1 -1
  20. package/dist/src/lib/flow-payload-validation.js +51 -0
  21. package/dist/src/lib/flow-payload-validation.js.map +1 -0
  22. package/dist/src/lib/flow-publish-reviewed-data.js +16 -0
  23. package/dist/src/lib/flow-publish-reviewed-data.js.map +1 -1
  24. package/dist/src/lib/flow-publish-version.js +182 -12
  25. package/dist/src/lib/flow-publish-version.js.map +1 -1
  26. package/dist/src/lib/identity-preflight.js +1021 -0
  27. package/dist/src/lib/identity-preflight.js.map +1 -0
  28. package/dist/src/lib/process-auto-build.js +147 -0
  29. package/dist/src/lib/process-auto-build.js.map +1 -1
  30. package/dist/src/lib/process-dedup-review.js +51 -0
  31. package/dist/src/lib/process-dedup-review.js.map +1 -1
  32. package/dist/src/lib/process-flow-build-plan.js +1071 -0
  33. package/dist/src/lib/process-flow-build-plan.js.map +1 -0
  34. package/dist/src/lib/process-payload-validation.js +14 -7
  35. package/dist/src/lib/process-payload-validation.js.map +1 -1
  36. package/dist/src/lib/process-publish-build.js +122 -4
  37. package/dist/src/lib/process-publish-build.js.map +1 -1
  38. package/dist/src/lib/process-refresh-references.js +19 -10
  39. package/dist/src/lib/process-refresh-references.js.map +1 -1
  40. package/dist/src/lib/process-required-fields.js +810 -0
  41. package/dist/src/lib/process-required-fields.js.map +1 -0
  42. package/dist/src/lib/process-save-draft-run.js +4 -1
  43. package/dist/src/lib/process-save-draft-run.js.map +1 -1
  44. package/dist/src/lib/publish.js +100 -0
  45. package/dist/src/lib/publish.js.map +1 -1
  46. package/dist/src/lib/review-flow.js +58 -0
  47. package/dist/src/lib/review-flow.js.map +1 -1
  48. package/dist/src/lib/review-process.js +150 -2
  49. package/dist/src/lib/review-process.js.map +1 -1
  50. package/dist/src/lib/runtime-rulesets.js +283 -0
  51. package/dist/src/lib/runtime-rulesets.js.map +1 -0
  52. package/package.json +2 -2
package/dist/src/cli.js CHANGED
@@ -21,7 +21,10 @@ import { runProcessDedupReview, } from './lib/process-dedup-review.js';
21
21
  import { runProcessResumeBuild, } from './lib/process-resume-build.js';
22
22
  import { runProcessPublishBuild, } from './lib/process-publish-build.js';
23
23
  import { runProcessSaveDraft, } from './lib/process-save-draft-run.js';
24
+ import { runProcessRequiredFieldsComplete, } from './lib/process-required-fields.js';
24
25
  import { runProcessVerifyRows, } from './lib/process-verify-rows.js';
26
+ import { runFlowBuildPlanMaterialize, runFlowBuildPlanValidate, runProcessBuildPlanMaterialize, runProcessBuildPlanValidate, } from './lib/process-flow-build-plan.js';
27
+ import { runFlowIdentityPreflight, runProcessIdentityPreflight, } from './lib/identity-preflight.js';
25
28
  import { runPublish } from './lib/publish.js';
26
29
  import { runProcessReview, } from './lib/review-process.js';
27
30
  import { runFlowReview, } from './lib/review-flow.js';
@@ -39,6 +42,13 @@ import { executeRemoteCommand, getRemoteCommandHelp } from './lib/remote.js';
39
42
  import { runValidation, } from './lib/validation.js';
40
43
  import { runDatasetValidate, } from './lib/dataset-validate.js';
41
44
  import { runDatasetReferencesRewrite, } from './lib/dataset-references-rewrite.js';
45
+ import { runDatasetRemoteRefresh, } from './lib/dataset-remote-refresh.js';
46
+ import { runDatasetRemoteVerify, } from './lib/dataset-remote-verify.js';
47
+ import { runDatasetBilingualApply, runDatasetBilingualExtract, runDatasetBilingualValidate, } from './lib/dataset-bilingual.js';
48
+ import { runDatasetEvidenceSearch, } from './lib/dataset-evidence-search.js';
49
+ import { runDatasetContract, } from './lib/dataset-contract.js';
50
+ import { runDatasetImportLcaConvert, } from './lib/dataset-import-lca.js';
51
+ import { runDatasetAuthor, } from './lib/dataset-author.js';
42
52
  function renderMainHelp(dotEnvStatus) {
43
53
  return `TianGong LCA CLI
44
54
 
@@ -57,9 +67,9 @@ Commands:
57
67
  Implemented Commands:
58
68
  doctor show environment diagnostics
59
69
  search flow | process | lifecyclemodel
60
- process get | list | scope-statistics | dedup-review | auto-build | resume-build | publish-build | save-draft | batch-build | refresh-references | verify-rows
61
- dataset validate | references rewrite
62
- flow get | list | fetch-rows | materialize-decisions | remediate | publish-version | publish-reviewed-data | build-alias-map | scan-process-flow-refs | plan-process-flow-repairs | apply-process-flow-repairs | regen-product | validate-processes
70
+ process get | list | identity-preflight | build-plan | scope-statistics | dedup-review | auto-build | resume-build | publish-build | complete-required-fields | save-draft | batch-build | refresh-references | verify-rows
71
+ dataset contract get | context-pack | import-lca convert | author | validate | verify-remote | bilingual extract/apply/validate | evidence-search plan/run | references rewrite/refresh-remote
72
+ flow get | list | identity-preflight | build-plan | fetch-rows | materialize-decisions | remediate | publish-version | publish-reviewed-data | build-alias-map | scan-process-flow-refs | plan-process-flow-repairs | apply-process-flow-repairs | regen-product | validate-processes
63
73
  lifecyclemodel auto-build | validate-build | publish-build | save-draft | graph | build-resulting-process | publish-resulting-process | orchestrate
64
74
  review process | flow | lifecyclemodel
65
75
  publish run
@@ -78,16 +88,29 @@ Examples:
78
88
  tiangong-lca search process --input ./request.json --dry-run
79
89
  tiangong-lca process get --id <process-id>
80
90
  tiangong-lca process list --state-code 100 --limit 20
91
+ tiangong-lca process identity-preflight --input ./process-preflight.json --out-dir ./process-preflight
92
+ tiangong-lca process build-plan validate --input ./process-build-plan.json --out-dir ./process-build-plan
81
93
  tiangong-lca process scope-statistics --out-dir /abs/path/to/process-scope --state-code 0 --state-code 100
82
94
  tiangong-lca process dedup-review --input ./duplicate-groups.json --out-dir /abs/path/to/process-dedup
83
95
  tiangong-lca process auto-build --input ./pff-request.json --out-dir /abs/path/to/process-run
84
96
  tiangong-lca process resume-build --run-dir /abs/path/to/process-run
85
97
  tiangong-lca process publish-build --run-dir /abs/path/to/process-run
98
+ tiangong-lca process complete-required-fields --input ./processes.jsonl --out ./processes.completed.jsonl --default-unit MJ
86
99
  tiangong-lca process save-draft --input ./patched-processes.jsonl --out-dir /abs/path/to/process-save-draft --dry-run
87
100
  tiangong-lca process batch-build --input ./batch-request.json --out-dir /abs/path/to/process-batch
88
101
  tiangong-lca process refresh-references --out-dir /abs/path/to/process-refresh --dry-run
89
102
  tiangong-lca process verify-rows --rows-file ./process-list-report.json --out-dir /abs/path/to/process-verify
90
103
  tiangong-lca dataset validate --input ./rows.jsonl --type auto --out-dir /abs/path/to/dataset-validate
104
+ tiangong-lca dataset contract get --type process --include schema,methodology,ruleset --out-dir ./contract
105
+ tiangong-lca dataset context-pack --type process --profile ai-import --out-dir ./context-pack
106
+ tiangong-lca dataset import-lca convert --input ./external-package --output-dir ./converted --from-format auto --target tidas
107
+ tiangong-lca dataset author --input ./source.pdf --target-types process,flow --out-dir ./authoring
108
+ tiangong-lca dataset verify-remote --input ./rows.jsonl --out-dir /abs/path/to/dataset-remote-verify
109
+ tiangong-lca dataset bilingual extract --input ./rows/processes.jsonl --type process --out-dir ./translation
110
+ tiangong-lca dataset bilingual apply --input ./rows/processes.jsonl --translations ./translation/trans-reviewed.jsonl --out ./rows/processes.translated.jsonl
111
+ tiangong-lca dataset bilingual validate --input ./rows/processes.translated.jsonl --type process --out-dir ./translation-validate
112
+ tiangong-lca dataset evidence-search plan --query "中国2026年电力结构数据" --out-dir ./evidence-search
113
+ tiangong-lca dataset evidence-search run --input ./evidence-search.request.json --results ./search-results.json --out-dir ./evidence-search
91
114
  tiangong-lca dataset references rewrite --input ./rows.jsonl --from flow:<old-id>@<old-version> --to flow:<new-id>@<new-version> --out-dir /abs/path/to/dataset-rewrite
92
115
  tiangong-lca lifecyclemodel auto-build --input ./lifecyclemodel-auto-build.request.json --out-dir /abs/path/to/lifecyclemodel-run
93
116
  tiangong-lca lifecyclemodel validate-build --run-dir /abs/path/to/lifecyclemodel-run
@@ -97,6 +120,8 @@ Examples:
97
120
  tiangong-lca lifecyclemodel orchestrate plan --input ./lifecyclemodel-orchestrate.request.json --out-dir /abs/path/to/lifecyclemodel-recursive-run
98
121
  tiangong-lca flow get --id <flow-id> --version <version>
99
122
  tiangong-lca flow list --id <flow-id> --state-code 100 --limit 20
123
+ tiangong-lca flow identity-preflight --input ./flow-preflight.json --out-dir ./flow-preflight
124
+ tiangong-lca flow build-plan validate --input ./flow-build-plan.json --out-dir ./flow-build-plan
100
125
  tiangong-lca flow fetch-rows --refs-file ./flow-refs.json --out-dir ./flow-fetch
101
126
  tiangong-lca flow materialize-decisions --decision-file ./approved-decisions.json --flow-rows-file ./review-input-rows.jsonl --out-dir ./flow-decisions
102
127
  tiangong-lca flow remediate --input-file ./invalid-flows.jsonl --out-dir ./flow-remediation
@@ -191,6 +216,13 @@ Options:
191
216
 
192
217
  Path rule:
193
218
  Relative out_dir values from the request body or --out-dir resolve from the request file directory.
219
+
220
+ Outputs written under out_dir:
221
+ - normalized-request.json
222
+ - collected-inputs.json
223
+ - relation-manifest.json
224
+ - verification-report.json
225
+ - publish-report.json
194
226
  `.trim();
195
227
  }
196
228
  function renderValidationHelp() {
@@ -210,12 +242,148 @@ function renderDatasetHelp() {
210
242
  tiangong-lca dataset <subcommand> [options]
211
243
 
212
244
  Implemented Subcommands:
245
+ contract get Write TIDAS schema / methodology / ruleset contract artifacts
246
+ context-pack Write an AI-ready TIDAS contract context pack
247
+ import-lca convert Convert supported external LCA packages through tidas-tools
248
+ author Extract source evidence and prepare TIDAS context packs for AI authoring
213
249
  validate Validate local flow / process / lifecyclemodel rows with the TIDAS SDK
250
+ verify-remote Verify dataset roots and TIDAS references against remote published versions
251
+ bilingual extract Extract bilingual translation units from local rows
252
+ bilingual apply Apply reviewed bilingual translations back to local rows
253
+ bilingual validate Validate bilingual rows with deterministic scans and schema/review gates
254
+ evidence-search Plan or record field-level public evidence retrieval
214
255
  references rewrite Rewrite flow references in local process and lifecyclemodel rows
256
+ references refresh-remote Refresh local TIDAS reference versions to latest reachable remote rows
215
257
 
216
258
  Examples:
259
+ tiangong-lca dataset contract get --type process --include schema,methodology,ruleset --out-dir ./contract --help
260
+ tiangong-lca dataset context-pack --type process --profile ai-import --out-dir ./context-pack --help
261
+ tiangong-lca dataset import-lca convert --input ./external-package --output-dir ./converted --from-format auto --target tidas --help
262
+ tiangong-lca dataset author --input ./source.pdf --target-types process,flow --out-dir ./authoring --help
217
263
  tiangong-lca dataset validate --input ./rows.jsonl --type auto --out-dir ./dataset-validate --help
264
+ tiangong-lca dataset verify-remote --input ./rows.jsonl --out-dir ./dataset-remote-verify --help
265
+ tiangong-lca dataset bilingual extract --input ./rows.jsonl --type process --out-dir ./translation --help
266
+ tiangong-lca dataset bilingual apply --input ./rows.jsonl --translations ./trans-reviewed.jsonl --out ./rows.translated.jsonl --help
267
+ tiangong-lca dataset bilingual validate --input ./rows.translated.jsonl --type process --out-dir ./translation-validate --help
268
+ tiangong-lca dataset evidence-search plan --query "中国2026年电力结构数据" --out-dir ./evidence-search --help
269
+ tiangong-lca dataset evidence-search run --input ./request.json --results ./search-results.json --out-dir ./evidence-search --help
218
270
  tiangong-lca dataset references rewrite --input ./rows.jsonl --from flow:<old-id>@<old-version> --to flow:<new-id>@<new-version> --out-dir ./dataset-rewrite --help
271
+ tiangong-lca dataset references refresh-remote --input ./rows.jsonl --out ./rows.refreshed.jsonl --out-dir ./dataset-reference-refresh --help
272
+ `.trim();
273
+ }
274
+ function renderDatasetContractHelp() {
275
+ return `Usage:
276
+ tiangong-lca dataset contract get --type <type> --out-dir <dir> [options]
277
+
278
+ Options:
279
+ --type <type> TIDAS target type: process, flow, source, contact, unitgroup, flowproperty, lifecyclemodel, lciamethod
280
+ --include <list> Comma-separated or repeatable list: schema, methodology, ruleset (default: all)
281
+ --profile <name> default | ai-import (default: default)
282
+ --out-dir <dir> Artifact directory
283
+ --json Print compact JSON
284
+ -h, --help
285
+
286
+ Outputs written under --out-dir:
287
+ - outputs/contract-manifest.json
288
+ - outputs/schema.json when requested and available
289
+ - outputs/methodology.yaml when requested and available
290
+ - outputs/runtime-ruleset.json when requested and available
291
+ - outputs/contract-report.json
292
+ `.trim();
293
+ }
294
+ function renderDatasetContextPackHelp() {
295
+ return `Usage:
296
+ tiangong-lca dataset context-pack --type <type> --out-dir <dir> [options]
297
+
298
+ Options:
299
+ --type <type> TIDAS target type for AI authoring or repair
300
+ --include <list> Comma-separated or repeatable list: schema, methodology, ruleset (default: all)
301
+ --profile <name> default | ai-import (default: ai-import)
302
+ --out-dir <dir> Artifact directory
303
+ --json Print compact JSON
304
+ -h, --help
305
+
306
+ Outputs written under --out-dir:
307
+ - outputs/contract-manifest.json
308
+ - outputs/schema.json
309
+ - outputs/methodology.yaml when available
310
+ - outputs/runtime-ruleset.json when available
311
+ - outputs/ai-context.json
312
+ - outputs/ai-context.md
313
+ - outputs/contract-report.json
314
+ `.trim();
315
+ }
316
+ function renderDatasetImportLcaHelp() {
317
+ return `Usage:
318
+ tiangong-lca dataset import-lca convert --input <path> --output-dir <dir> [options]
319
+
320
+ Options:
321
+ --input <path> Source file, directory, or package to import
322
+ --output-dir <dir> Output directory for generated package and reports
323
+ --from-format <format> auto, ecospold1, ecospold2, openlca-jsonld, openlca-process-xlsx, simapro-csv
324
+ --target <target> tidas | ilcd | both (default: tidas)
325
+ --report <file> Conversion report path (default: <output-dir>/conversion-report.json)
326
+ --mapping-dir <dir> Optional custom mapping/reference data directory
327
+ --language <lang> Default language for generated text (default: en)
328
+ --validation-jobs <n> Parallel validation jobs passed to tidas-tools (default: 1)
329
+ --detect-only Only detect the input format and write the report
330
+ --fail-on-warning Return non-zero when converter warnings are present
331
+ --python <bin> Python executable (default: python3)
332
+ --tidas-tools-dir <dir> Explicit tidas-tools checkout path
333
+ --json Print compact JSON
334
+ -h, --help
335
+
336
+ Outputs written under --output-dir:
337
+ - conversion-report.json
338
+ - tidas/ when target includes TIDAS and not detect-only
339
+ - ilcd/ when target includes ILCD and not detect-only
340
+ - mapping.csv when not detect-only
341
+ - outputs/import-lca-report.json
342
+ `.trim();
343
+ }
344
+ function renderDatasetAuthorHelp() {
345
+ return `Usage:
346
+ tiangong-lca dataset author --input <file> --target-types <types> --out-dir <dir> [options]
347
+
348
+ Options:
349
+ --input <file> PDF, Excel, image, markdown, or source document file
350
+ --target-types <list> Comma-separated or repeatable TIDAS types, for example process,flow,source
351
+ --out-dir <dir> Artifact directory
352
+ --prompt <text> Optional extraction prompt passed to the unstructured parser
353
+ --provider <name> Optional unstructured parser provider override
354
+ --model <name> Optional unstructured parser model override
355
+ --timeout-ms <n> Parser timeout in milliseconds (default: 120000)
356
+ --json Print compact JSON
357
+ -h, --help
358
+
359
+ Outputs written under --out-dir:
360
+ - outputs/source-extract.json
361
+ - context/<type>/outputs/contract-manifest.json
362
+ - context/<type>/outputs/ai-context.json
363
+ - outputs/authoring-report.json
364
+
365
+ Environment:
366
+ TIANGONG_LCA_UNSTRUCTURED_API_BASE_URL and TIANGONG_LCA_UNSTRUCTURED_API_KEY
367
+ `.trim();
368
+ }
369
+ function renderDatasetRemoteVerifyHelp() {
370
+ return `Usage:
371
+ tiangong-lca dataset verify-remote --input <file> --out-dir <dir> [options]
372
+
373
+ Options:
374
+ --input <file> Local rows as JSON or JSONL; objects with rows[] are also accepted
375
+ --out-dir <dir> Artifact directory for the remote verification report
376
+ --root-policy <mode> existing | candidate (default: existing)
377
+ --json Print compact JSON
378
+ -h, --help
379
+
380
+ Environment:
381
+ TIANGONG_LCA_API_BASE_URL, TIANGONG_LCA_API_KEY, and TIANGONG_LCA_SUPABASE_PUBLISHABLE_KEY
382
+
383
+ Outputs written under --out-dir:
384
+ - outputs/remote-verification-report.json
385
+ - outputs/remote-verification.jsonl
386
+ - outputs/blockers.jsonl
219
387
  `.trim();
220
388
  }
221
389
  function renderDatasetValidateHelp() {
@@ -235,21 +403,135 @@ Outputs written under --out-dir:
235
403
  - outputs/invalid-rows.jsonl
236
404
  `.trim();
237
405
  }
406
+ function renderDatasetBilingualHelp() {
407
+ return `Usage:
408
+ tiangong-lca dataset bilingual <extract|apply|validate> [options]
409
+
410
+ Subcommands:
411
+ extract Extract trans-units.jsonl from process/flow/lifecyclemodel rows
412
+ apply Apply reviewed translations to rows and write translation-evidence.json
413
+ validate Run placeholder/mixed-language scans plus schema and process/flow review gates
414
+
415
+ Examples:
416
+ tiangong-lca dataset bilingual extract --input ./rows/processes.jsonl --type process --out-dir ./translation
417
+ tiangong-lca dataset bilingual apply --input ./rows/processes.jsonl --translations ./translation/trans-reviewed.jsonl --out ./rows/processes.translated.jsonl
418
+ tiangong-lca dataset bilingual validate --input ./rows/processes.translated.jsonl --type process --out-dir ./translation-validate
419
+ `.trim();
420
+ }
421
+ function renderDatasetBilingualExtractHelp() {
422
+ return `Usage:
423
+ tiangong-lca dataset bilingual extract --input <file> [options]
424
+
425
+ Options:
426
+ --input <file> Local rows as JSON or JSONL
427
+ --type <type> auto | flow | process | lifecyclemodel (default: auto)
428
+ --source-lang <lang> Source language code (default: en)
429
+ --target-lang <lang> Target language code (default: zh)
430
+ --out-dir <dir> Artifact directory for trans-units.jsonl and extract-report.json
431
+ --json Print compact JSON
432
+ -h, --help
433
+
434
+ Outputs written under --out-dir:
435
+ - outputs/trans-units.jsonl
436
+ - outputs/extract-report.json
437
+ `.trim();
438
+ }
439
+ function renderDatasetBilingualApplyHelp() {
440
+ return `Usage:
441
+ tiangong-lca dataset bilingual apply --input <file> --translations <file> --out <file> [options]
442
+
443
+ Options:
444
+ --input <file> Local rows as JSON or JSONL
445
+ --translations <file> Reviewed translation JSONL from extract units
446
+ --out <file> Output JSONL with translations applied
447
+ --target-lang <lang> Target language code (default: zh)
448
+ --out-dir <dir> Optional artifact directory; defaults to the output file directory
449
+ --json Print compact JSON
450
+ -h, --help
451
+
452
+ Translation row fields:
453
+ unit_id, row_index, field_path, source_lang, target_lang, source_text, translated_text,
454
+ basis, review_status, reviewer
455
+
456
+ Outputs:
457
+ - translated rows at --out
458
+ - outputs/translation-evidence.json
459
+ - outputs/bilingual-apply-report.json
460
+ `.trim();
461
+ }
462
+ function renderDatasetBilingualValidateHelp() {
463
+ return `Usage:
464
+ tiangong-lca dataset bilingual validate --input <file> [options]
465
+
466
+ Options:
467
+ --input <file> Local rows as JSON or JSONL
468
+ --type <type> auto | flow | process | lifecyclemodel (default: auto)
469
+ --out-dir <dir> Artifact directory for scan, schema, and review outputs
470
+ --json Print compact JSON
471
+ -h, --help
472
+
473
+ Outputs written under --out-dir:
474
+ - outputs/bilingual-validate-report.json
475
+ - outputs/bilingual-findings.jsonl
476
+ - schema/outputs/validation-report.json
477
+ - review/process/... and/or review/flow/... when applicable
478
+ `.trim();
479
+ }
480
+ function renderDatasetEvidenceSearchHelp() {
481
+ return `Usage:
482
+ tiangong-lca dataset evidence-search <plan|run> [options]
483
+
484
+ Plan:
485
+ tiangong-lca dataset evidence-search plan --query <text> --out-dir <dir>
486
+
487
+ Run:
488
+ tiangong-lca dataset evidence-search run --input <request.json> --results <search-results.json> --out-dir <dir>
489
+ tiangong-lca dataset evidence-search run --query <text> --provider-url <url> --out-dir <dir>
490
+
491
+ Options:
492
+ --query <text> Evidence question when no --input is supplied
493
+ --input <file> JSON request with question, field, budget, preferred_domains, and required_evidence
494
+ --results <file> Normalized external search results JSON/JSONL captured from web/search tools
495
+ --provider-url <url> Optional generic JSON search provider endpoint
496
+ --provider-key <key> Optional bearer token for --provider-url
497
+ --profile <profile> shallow | balanced | deep (default: balanced)
498
+ --max-queries <n> Override query budget
499
+ --max-results-per-query <n> Override per-query result budget
500
+ --timeout-ms <n> Provider request timeout in milliseconds
501
+ --out-dir <dir> Artifact directory
502
+ --json Print compact JSON
503
+ -h, --help
504
+
505
+ Outputs written under --out-dir:
506
+ - outputs/evidence-search-plan.json
507
+ - outputs/evidence-search-results.jsonl
508
+ - outputs/evidence-search-report.json
509
+ - outputs/evidence-search-declaration.json when evidence is absent or only partial
510
+ `.trim();
511
+ }
238
512
  function renderDatasetReferencesHelp() {
239
513
  return `Usage:
514
+ tiangong-lca dataset references <rewrite|refresh-remote> [options]
515
+
516
+ Rewrite:
240
517
  tiangong-lca dataset references rewrite --input <file> --from flow:<id>[@<version>] --to flow:<id>[@<version>] [options]
241
518
 
519
+ Refresh remote versions:
520
+ tiangong-lca dataset references refresh-remote --input <file> --out <file> --out-dir <dir> [options]
521
+
242
522
  Options:
243
523
  --input <file> Local rows as JSON or JSONL; process and lifecyclemodel rows are supported
244
524
  --from <ref> Source flow reference, for example flow:<old-id>@01.00.000
245
525
  --to <ref> Target flow reference, for example flow:<new-id>@01.01.000
246
- --type <type> Repeatable row type filter: process | lifecyclemodel (default: both)
247
- --types <csv> Comma-separated alias for one or more row types
248
- --scope <label> Optional artifact label for the already-frozen input scope
249
- --out-dir <dir> Artifact directory for rewrite plan and patched rows
250
- --commit Execute state-aware save-draft writes for patched rows
251
- --dry-run Keep the command local-only (default)
252
- --json Print compact JSON
526
+ --out <file> Output JSONL path for refresh-remote patched rows
527
+ --root-policy <mode> existing | candidate for refresh-remote root rows (default: existing)
528
+ --type <type> Repeatable row type filter: process | lifecyclemodel (default: both)
529
+ --types <csv> Comma-separated alias for one or more row types
530
+ --scope <label> Optional artifact label for the already-frozen input scope
531
+ --out-dir <dir> Artifact directory for rewrite plan and patched rows
532
+ --commit Execute state-aware save-draft writes for patched rows
533
+ --dry-run Keep the command local-only (default)
534
+ --json Print compact JSON
253
535
  -h, --help
254
536
 
255
537
  Environment:
@@ -261,6 +543,10 @@ Outputs written under --out-dir:
261
543
  - outputs/patched-rows.jsonl
262
544
  - outputs/rewrite-plan.json
263
545
  - outputs/summary.json
546
+ - outputs/remote-refresh-report.json
547
+ - outputs/remote-refresh-patches.jsonl
548
+ - pre-refresh-verify/outputs/remote-verification-report.json
549
+ - post-refresh-verify/outputs/remote-verification-report.json
264
550
  `.trim();
265
551
  }
266
552
  function renderFlowHelp() {
@@ -270,6 +556,8 @@ function renderFlowHelp() {
270
556
  Implemented Subcommands:
271
557
  get Load one flow dataset by identifier through direct Supabase access
272
558
  list Enumerate flow datasets through direct Supabase access with deterministic filters
559
+ identity-preflight Compare one target flow against local candidates before generation
560
+ build-plan Validate or materialize a flow build plan into gate artifacts
273
561
  fetch-rows Materialize real DB flow refs into local review-input rows and fetch artifacts
274
562
  materialize-decisions Materialize approved merge decisions into canonical-map, rewrite-plan, and seed artifacts
275
563
  remediate Deterministically repair invalid local flow rows and emit artifact-first outputs
@@ -286,6 +574,8 @@ Examples:
286
574
  tiangong-lca flow --help
287
575
  tiangong-lca flow get --help
288
576
  tiangong-lca flow list --help
577
+ tiangong-lca flow identity-preflight --help
578
+ tiangong-lca flow build-plan validate --help
289
579
  tiangong-lca flow fetch-rows --help
290
580
  tiangong-lca flow materialize-decisions --help
291
581
  tiangong-lca flow remediate --help
@@ -299,6 +589,52 @@ Examples:
299
589
  tiangong-lca flow validate-processes --help
300
590
  `.trim();
301
591
  }
592
+ function renderFlowBuildPlanHelp() {
593
+ return `Usage:
594
+ tiangong-lca flow build-plan <validate|materialize> --input <file> [options]
595
+
596
+ Options:
597
+ --input <file> JSON flow build plan; materialize writes a canonical flowDataSet
598
+ --out-dir <dir> Optional artifact directory for gate outputs
599
+ --report-only Print blocker reports with exit code 0
600
+ --json Print compact JSON
601
+ -h, --help
602
+
603
+ Outputs written under --out-dir:
604
+ - outputs/build-plan-gate-report.json
605
+ - outputs/materialized-flow.json
606
+ `.trim();
607
+ }
608
+ function renderFlowIdentityPreflightHelp() {
609
+ return `Usage:
610
+ tiangong-lca flow identity-preflight --input <file> [options]
611
+
612
+ Options:
613
+ --input <file> JSON preflight request with target flow and optional candidates
614
+ --candidate-input <path>
615
+ Optional JSON/JSONL file or directory of candidate flow rows; repeatable
616
+ --remote-candidates
617
+ Also fetch candidate rows from flow_hybrid_search
618
+ --remote-query <text>
619
+ Override the remote search query; defaults to target identity text
620
+ --remote-limit <n>
621
+ Limit remote candidate rows after fetch
622
+ --out-dir <dir> Optional artifact directory for identity decision outputs
623
+ --json Print compact JSON
624
+ -h, --help
625
+
626
+ Input contract:
627
+ {
628
+ "target": { "...": "flow target or canonical flowDataSet" },
629
+ "candidates": [{ "...": "existing flow row or canonical flowDataSet" }]
630
+ }
631
+
632
+ Outputs written under --out-dir:
633
+ - outputs/identity-decision.json
634
+ - outputs/identity-candidates.jsonl
635
+ - outputs/identity-candidate-sources.json
636
+ `.trim();
637
+ }
302
638
  function renderFlowGetHelp() {
303
639
  return `Usage:
304
640
  tiangong-lca flow get --id <flow-id> [options]
@@ -435,6 +771,7 @@ Environment:
435
771
  Outputs written under --out-dir:
436
772
  - flows_tidas_sdk_plus_classification_mcp_success_list.json
437
773
  - flows_tidas_sdk_plus_classification_remote_validation_failed.jsonl
774
+ - flow-publish-version-gate-report.json
438
775
  - flows_tidas_sdk_plus_classification_mcp_sync_report.json
439
776
  `.trim();
440
777
  }
@@ -470,6 +807,7 @@ Outputs written under --out-dir:
470
807
  - publish-report.json
471
808
  - flows_tidas_sdk_plus_classification_mcp_success_list.json
472
809
  - flows_tidas_sdk_plus_classification_remote_validation_failed.jsonl
810
+ - flow-publish-version-gate-report.json
473
811
  - flows_tidas_sdk_plus_classification_mcp_sync_report.json
474
812
  `.trim();
475
813
  }
@@ -949,6 +1287,36 @@ Outputs written under --out-dir:
949
1287
  - reports/process-scope-statistics.zh-CN.md
950
1288
  `.trim();
951
1289
  }
1290
+ function renderProcessIdentityPreflightHelp() {
1291
+ return `Usage:
1292
+ tiangong-lca process identity-preflight --input <file> [options]
1293
+
1294
+ Options:
1295
+ --input <file> JSON preflight request with target process and optional candidates
1296
+ --candidate-input <path>
1297
+ Optional JSON/JSONL file or directory of candidate process rows; repeatable
1298
+ --remote-candidates
1299
+ Also fetch candidate rows from process_hybrid_search
1300
+ --remote-query <text>
1301
+ Override the remote search query; defaults to target identity text
1302
+ --remote-limit <n>
1303
+ Limit remote candidate rows after fetch
1304
+ --out-dir <dir> Optional artifact directory for identity decision outputs
1305
+ --json Print compact JSON
1306
+ -h, --help
1307
+
1308
+ Input contract:
1309
+ {
1310
+ "target": { "...": "process target or canonical processDataSet" },
1311
+ "candidates": [{ "...": "existing process row or canonical processDataSet" }]
1312
+ }
1313
+
1314
+ Outputs written under --out-dir:
1315
+ - outputs/identity-decision.json
1316
+ - outputs/identity-candidates.jsonl
1317
+ - outputs/identity-candidate-sources.json
1318
+ `.trim();
1319
+ }
952
1320
  function renderProcessDedupReviewHelp() {
953
1321
  return `Usage:
954
1322
  tiangong-lca process dedup-review --input <file> --out-dir <dir> [options]
@@ -1045,6 +1413,30 @@ Outputs written under --out-dir:
1045
1413
  - outputs/save-draft-rpc/summary.json
1046
1414
  `.trim();
1047
1415
  }
1416
+ function renderProcessRequiredFieldsHelp() {
1417
+ return `Usage:
1418
+ tiangong-lca process complete-required-fields --input <file> --out <file> [options]
1419
+
1420
+ Options:
1421
+ --input <file> Process rows JSON/JSONL file
1422
+ --out <file> Output JSONL with required fields completed
1423
+ --out-dir <dir> Optional artifact directory for report and evidence
1424
+ --flows <file> Optional flow rows JSON/JSONL used to infer reference-flow units
1425
+ --default-unit <unit> Unit suffix to use when it cannot be inferred (default: unit)
1426
+ --json Print compact JSON
1427
+ -h, --help
1428
+
1429
+ Annual supply / production volume policy:
1430
+ 1. keep an existing valid annualized annualSupplyOrProductionVolume, for example "3.6 MJ/year";
1431
+ 2. use an explicit value from row-level authoring evidence or evidenceManifest field bindings;
1432
+ 3. otherwise complete from the quantitative reference flow meanAmount/resultingAmount and unit.
1433
+
1434
+ Outputs:
1435
+ - completed rows at --out
1436
+ - outputs/process-required-fields-report.json
1437
+ - outputs/process-required-fields-evidence.jsonl
1438
+ `.trim();
1439
+ }
1048
1440
  function renderProcessRefreshReferencesHelp() {
1049
1441
  return `Usage:
1050
1442
  tiangong-lca process refresh-references --out-dir <dir> [options]
@@ -1115,11 +1507,14 @@ function renderProcessHelp() {
1115
1507
  Implemented Subcommands:
1116
1508
  get Load one process dataset by identifier through direct Supabase access
1117
1509
  list List visible process rows through direct Supabase access
1510
+ identity-preflight Compare one target process against local candidates before generation
1511
+ build-plan Validate or materialize a process build plan into gate artifacts
1118
1512
  scope-statistics Count repeatable coverage statistics from visible or owner-filtered process snapshots
1119
1513
  dedup-review Review grouped duplicate process candidates and emit keep/delete evidence
1120
1514
  auto-build Prepare a local process-from-flow run scaffold and artifact workspace
1121
1515
  resume-build Prepare a local resume handoff from one existing process build run
1122
1516
  publish-build Prepare publish handoff artifacts from one existing process build run
1517
+ complete-required-fields Complete deterministic required authoring fields in process rows
1123
1518
  save-draft Save canonical process datasets through the state-aware draft-maintenance path
1124
1519
  batch-build Run multiple process auto-build requests through one batch-oriented CLI surface
1125
1520
  refresh-references Refresh current-user process references to the latest reachable dataset versions
@@ -1129,17 +1524,36 @@ Examples:
1129
1524
  tiangong-lca process --help
1130
1525
  tiangong-lca process get --id <process-id>
1131
1526
  tiangong-lca process list --state-code 100 --limit 20 --help
1527
+ tiangong-lca process identity-preflight --input ./process-preflight.json --help
1528
+ tiangong-lca process build-plan validate --input ./process-build-plan.json --help
1132
1529
  tiangong-lca process scope-statistics --out-dir ./process-scope --state-code 0 --state-code 100 --help
1133
1530
  tiangong-lca process dedup-review --input ./duplicate-groups.json --out-dir ./process-dedup --help
1134
1531
  tiangong-lca process auto-build --help
1135
1532
  tiangong-lca process resume-build --run-dir /abs/path/to/process-run --help
1136
1533
  tiangong-lca process publish-build --run-dir /abs/path/to/process-run --help
1534
+ tiangong-lca process complete-required-fields --input ./processes.jsonl --out ./processes.completed.jsonl --help
1137
1535
  tiangong-lca process save-draft --input ./patched-processes.jsonl --help
1138
1536
  tiangong-lca process batch-build --input ./batch-request.json --help
1139
1537
  tiangong-lca process refresh-references --out-dir ./process-refresh --help
1140
1538
  tiangong-lca process verify-rows --rows-file ./process-list-report.json --out-dir ./process-verify --help
1141
1539
  `.trim();
1142
1540
  }
1541
+ function renderProcessBuildPlanHelp() {
1542
+ return `Usage:
1543
+ tiangong-lca process build-plan <validate|materialize> --input <file> [options]
1544
+
1545
+ Options:
1546
+ --input <file> JSON process build plan; materialize writes a canonical processDataSet
1547
+ --out-dir <dir> Optional artifact directory for gate outputs
1548
+ --report-only Print blocker reports with exit code 0
1549
+ --json Print compact JSON
1550
+ -h, --help
1551
+
1552
+ Outputs written under --out-dir:
1553
+ - outputs/build-plan-gate-report.json
1554
+ - outputs/materialized-process.json
1555
+ `.trim();
1556
+ }
1143
1557
  function renderDoctorText(report) {
1144
1558
  const lines = [
1145
1559
  'TianGong CLI doctor',
@@ -1371,11 +1785,479 @@ function parseDatasetValidateFlags(args) {
1371
1785
  help: Boolean(values.help),
1372
1786
  json: Boolean(values.json),
1373
1787
  inputPath: typeof values.input === 'string' ? values.input : '',
1374
- type: typeof values.type === 'string' ? values.type : undefined,
1788
+ type: typeof values.type === 'string' ? values.type : undefined,
1789
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
1790
+ };
1791
+ }
1792
+ function parseDatasetContractFlags(args) {
1793
+ let values;
1794
+ try {
1795
+ ({ values } = parseArgs({
1796
+ args,
1797
+ allowPositionals: false,
1798
+ strict: true,
1799
+ options: {
1800
+ help: { type: 'boolean', short: 'h' },
1801
+ json: { type: 'boolean' },
1802
+ type: { type: 'string' },
1803
+ include: { type: 'string', multiple: true },
1804
+ profile: { type: 'string' },
1805
+ 'out-dir': { type: 'string' },
1806
+ },
1807
+ }));
1808
+ }
1809
+ catch (error) {
1810
+ throw new CliError(String(error), {
1811
+ code: 'INVALID_ARGS',
1812
+ exitCode: 2,
1813
+ });
1814
+ }
1815
+ return {
1816
+ help: Boolean(values.help),
1817
+ json: Boolean(values.json),
1818
+ type: typeof values.type === 'string' ? values.type : undefined,
1819
+ include: Array.isArray(values.include)
1820
+ ? values.include.filter((value) => typeof value === 'string')
1821
+ : [],
1822
+ profile: typeof values.profile === 'string' ? values.profile : undefined,
1823
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
1824
+ };
1825
+ }
1826
+ function parseDatasetImportLcaConvertFlags(args) {
1827
+ let values;
1828
+ try {
1829
+ ({ values } = parseArgs({
1830
+ args,
1831
+ allowPositionals: false,
1832
+ strict: true,
1833
+ options: {
1834
+ help: { type: 'boolean', short: 'h' },
1835
+ json: { type: 'boolean' },
1836
+ input: { type: 'string' },
1837
+ 'output-dir': { type: 'string' },
1838
+ 'from-format': { type: 'string' },
1839
+ target: { type: 'string' },
1840
+ report: { type: 'string' },
1841
+ 'mapping-dir': { type: 'string' },
1842
+ language: { type: 'string' },
1843
+ 'validation-jobs': { type: 'string' },
1844
+ 'detect-only': { type: 'boolean' },
1845
+ 'fail-on-warning': { type: 'boolean' },
1846
+ python: { type: 'string' },
1847
+ 'tidas-tools-dir': { type: 'string' },
1848
+ },
1849
+ }));
1850
+ }
1851
+ catch (error) {
1852
+ throw new CliError(String(error), {
1853
+ code: 'INVALID_ARGS',
1854
+ exitCode: 2,
1855
+ });
1856
+ }
1857
+ const validationJobs = typeof values['validation-jobs'] === 'string' ? Number(values['validation-jobs']) : undefined;
1858
+ if (validationJobs !== undefined && (!Number.isInteger(validationJobs) || validationJobs < 0)) {
1859
+ throw new CliError('--validation-jobs must be a non-negative integer.', {
1860
+ code: 'DATASET_IMPORT_LCA_VALIDATION_JOBS_INVALID',
1861
+ exitCode: 2,
1862
+ });
1863
+ }
1864
+ return {
1865
+ help: Boolean(values.help),
1866
+ json: Boolean(values.json),
1867
+ inputPath: typeof values.input === 'string' ? values.input : '',
1868
+ outputDir: typeof values['output-dir'] === 'string' ? values['output-dir'] : '',
1869
+ fromFormat: typeof values['from-format'] === 'string' ? values['from-format'] : undefined,
1870
+ target: typeof values.target === 'string' ? values.target : undefined,
1871
+ reportPath: typeof values.report === 'string' ? values.report : undefined,
1872
+ mappingDir: typeof values['mapping-dir'] === 'string' ? values['mapping-dir'] : undefined,
1873
+ language: typeof values.language === 'string' ? values.language : undefined,
1874
+ validationJobs,
1875
+ detectOnly: Boolean(values['detect-only']),
1876
+ failOnWarning: Boolean(values['fail-on-warning']),
1877
+ pythonBin: typeof values.python === 'string' ? values.python : undefined,
1878
+ tidasToolsDir: typeof values['tidas-tools-dir'] === 'string' ? values['tidas-tools-dir'] : undefined,
1879
+ };
1880
+ }
1881
+ function parseDatasetAuthorFlags(args) {
1882
+ let values;
1883
+ try {
1884
+ ({ values } = parseArgs({
1885
+ args,
1886
+ allowPositionals: false,
1887
+ strict: true,
1888
+ options: {
1889
+ help: { type: 'boolean', short: 'h' },
1890
+ json: { type: 'boolean' },
1891
+ input: { type: 'string' },
1892
+ 'target-types': { type: 'string', multiple: true },
1893
+ 'out-dir': { type: 'string' },
1894
+ prompt: { type: 'string' },
1895
+ provider: { type: 'string' },
1896
+ model: { type: 'string' },
1897
+ 'timeout-ms': { type: 'string' },
1898
+ },
1899
+ }));
1900
+ }
1901
+ catch (error) {
1902
+ throw new CliError(String(error), {
1903
+ code: 'INVALID_ARGS',
1904
+ exitCode: 2,
1905
+ });
1906
+ }
1907
+ const timeoutMs = typeof values['timeout-ms'] === 'string' ? Number(values['timeout-ms']) : undefined;
1908
+ if (timeoutMs !== undefined && (!Number.isInteger(timeoutMs) || timeoutMs <= 0)) {
1909
+ throw new CliError('--timeout-ms must be a positive integer.', {
1910
+ code: 'DATASET_AUTHOR_TIMEOUT_INVALID',
1911
+ exitCode: 2,
1912
+ });
1913
+ }
1914
+ return {
1915
+ help: Boolean(values.help),
1916
+ json: Boolean(values.json),
1917
+ inputPath: typeof values.input === 'string' ? values.input : '',
1918
+ targetTypes: Array.isArray(values['target-types'])
1919
+ ? values['target-types'].filter((value) => typeof value === 'string')
1920
+ : [],
1921
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
1922
+ prompt: typeof values.prompt === 'string' ? values.prompt : undefined,
1923
+ provider: typeof values.provider === 'string' ? values.provider : undefined,
1924
+ model: typeof values.model === 'string' ? values.model : undefined,
1925
+ timeoutMs,
1926
+ };
1927
+ }
1928
+ function parseDatasetRemoteVerifyFlags(args) {
1929
+ let values;
1930
+ try {
1931
+ ({ values } = parseArgs({
1932
+ args,
1933
+ allowPositionals: false,
1934
+ strict: true,
1935
+ options: {
1936
+ help: { type: 'boolean', short: 'h' },
1937
+ json: { type: 'boolean' },
1938
+ input: { type: 'string' },
1939
+ 'out-dir': { type: 'string' },
1940
+ 'root-policy': { type: 'string' },
1941
+ },
1942
+ }));
1943
+ }
1944
+ catch (error) {
1945
+ throw new CliError(String(error), {
1946
+ code: 'INVALID_ARGS',
1947
+ exitCode: 2,
1948
+ });
1949
+ }
1950
+ const rawRootPolicy = typeof values['root-policy'] === 'string' ? values['root-policy'] : 'existing';
1951
+ if (!['existing', 'candidate'].includes(rawRootPolicy)) {
1952
+ throw new CliError("--root-policy must be 'existing' or 'candidate'.", {
1953
+ code: 'DATASET_REMOTE_VERIFY_ROOT_POLICY_INVALID',
1954
+ exitCode: 2,
1955
+ });
1956
+ }
1957
+ const rootPolicy = rawRootPolicy;
1958
+ return {
1959
+ help: Boolean(values.help),
1960
+ json: Boolean(values.json),
1961
+ inputPath: typeof values.input === 'string' ? values.input : '',
1962
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
1963
+ rootPolicy,
1964
+ };
1965
+ }
1966
+ function parseDatasetBilingualExtractFlags(args) {
1967
+ let values;
1968
+ try {
1969
+ ({ values } = parseArgs({
1970
+ args,
1971
+ allowPositionals: false,
1972
+ strict: true,
1973
+ options: {
1974
+ help: { type: 'boolean', short: 'h' },
1975
+ json: { type: 'boolean' },
1976
+ input: { type: 'string' },
1977
+ type: { type: 'string' },
1978
+ 'source-lang': { type: 'string' },
1979
+ 'target-lang': { type: 'string' },
1980
+ 'out-dir': { type: 'string' },
1981
+ },
1982
+ }));
1983
+ }
1984
+ catch (error) {
1985
+ throw new CliError(String(error), {
1986
+ code: 'INVALID_ARGS',
1987
+ exitCode: 2,
1988
+ });
1989
+ }
1990
+ return {
1991
+ help: Boolean(values.help),
1992
+ json: Boolean(values.json),
1993
+ inputPath: typeof values.input === 'string' ? values.input : '',
1994
+ type: typeof values.type === 'string' ? values.type : undefined,
1995
+ sourceLang: typeof values['source-lang'] === 'string' ? values['source-lang'] : null,
1996
+ targetLang: typeof values['target-lang'] === 'string' ? values['target-lang'] : null,
1997
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
1998
+ };
1999
+ }
2000
+ function parseDatasetBilingualApplyFlags(args) {
2001
+ let values;
2002
+ try {
2003
+ ({ values } = parseArgs({
2004
+ args,
2005
+ allowPositionals: false,
2006
+ strict: true,
2007
+ options: {
2008
+ help: { type: 'boolean', short: 'h' },
2009
+ json: { type: 'boolean' },
2010
+ input: { type: 'string' },
2011
+ translations: { type: 'string' },
2012
+ out: { type: 'string' },
2013
+ 'target-lang': { type: 'string' },
2014
+ 'out-dir': { type: 'string' },
2015
+ },
2016
+ }));
2017
+ }
2018
+ catch (error) {
2019
+ throw new CliError(String(error), {
2020
+ code: 'INVALID_ARGS',
2021
+ exitCode: 2,
2022
+ });
2023
+ }
2024
+ return {
2025
+ help: Boolean(values.help),
2026
+ json: Boolean(values.json),
2027
+ inputPath: typeof values.input === 'string' ? values.input : '',
2028
+ translationsPath: typeof values.translations === 'string' ? values.translations : '',
2029
+ outPath: typeof values.out === 'string' ? values.out : '',
2030
+ targetLang: typeof values['target-lang'] === 'string' ? values['target-lang'] : null,
2031
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
2032
+ };
2033
+ }
2034
+ function parseDatasetBilingualValidateFlags(args) {
2035
+ let values;
2036
+ try {
2037
+ ({ values } = parseArgs({
2038
+ args,
2039
+ allowPositionals: false,
2040
+ strict: true,
2041
+ options: {
2042
+ help: { type: 'boolean', short: 'h' },
2043
+ json: { type: 'boolean' },
2044
+ input: { type: 'string' },
2045
+ type: { type: 'string' },
2046
+ 'out-dir': { type: 'string' },
2047
+ },
2048
+ }));
2049
+ }
2050
+ catch (error) {
2051
+ throw new CliError(String(error), {
2052
+ code: 'INVALID_ARGS',
2053
+ exitCode: 2,
2054
+ });
2055
+ }
2056
+ return {
2057
+ help: Boolean(values.help),
2058
+ json: Boolean(values.json),
2059
+ inputPath: typeof values.input === 'string' ? values.input : '',
2060
+ type: typeof values.type === 'string' ? values.type : undefined,
2061
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
2062
+ };
2063
+ }
2064
+ function parseDatasetEvidenceSearchFlags(args) {
2065
+ let values;
2066
+ try {
2067
+ ({ values } = parseArgs({
2068
+ args,
2069
+ allowPositionals: false,
2070
+ strict: true,
2071
+ options: {
2072
+ help: { type: 'boolean', short: 'h' },
2073
+ json: { type: 'boolean' },
2074
+ query: { type: 'string' },
2075
+ input: { type: 'string' },
2076
+ results: { type: 'string' },
2077
+ 'provider-url': { type: 'string' },
2078
+ 'provider-key': { type: 'string' },
2079
+ profile: { type: 'string' },
2080
+ 'out-dir': { type: 'string' },
2081
+ 'max-queries': { type: 'string' },
2082
+ 'max-results-per-query': { type: 'string' },
2083
+ 'timeout-ms': { type: 'string' },
2084
+ },
2085
+ }));
2086
+ }
2087
+ catch (error) {
2088
+ throw new CliError(String(error), {
2089
+ code: 'INVALID_ARGS',
2090
+ exitCode: 2,
2091
+ });
2092
+ }
2093
+ const readNumber = (value) => {
2094
+ if (typeof value !== 'string') {
2095
+ return null;
2096
+ }
2097
+ const parsed = Number(value);
2098
+ return Number.isFinite(parsed) ? parsed : Number.NaN;
2099
+ };
2100
+ return {
2101
+ help: Boolean(values.help),
2102
+ json: Boolean(values.json),
2103
+ query: typeof values.query === 'string' ? values.query : null,
2104
+ inputPath: typeof values.input === 'string' ? values.input : null,
2105
+ resultsPath: typeof values.results === 'string' ? values.results : null,
2106
+ providerUrl: typeof values['provider-url'] === 'string' ? values['provider-url'] : null,
2107
+ providerKey: typeof values['provider-key'] === 'string' ? values['provider-key'] : null,
2108
+ profile: typeof values.profile === 'string' ? values.profile : null,
2109
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
2110
+ maxQueries: readNumber(values['max-queries']),
2111
+ maxResultsPerQuery: readNumber(values['max-results-per-query']),
2112
+ timeoutMs: readNumber(values['timeout-ms']),
2113
+ };
2114
+ }
2115
+ function parseDatasetReferencesRewriteFlags(args) {
2116
+ let values;
2117
+ try {
2118
+ ({ values } = parseArgs({
2119
+ args,
2120
+ allowPositionals: false,
2121
+ strict: true,
2122
+ options: {
2123
+ help: { type: 'boolean', short: 'h' },
2124
+ json: { type: 'boolean' },
2125
+ input: { type: 'string' },
2126
+ from: { type: 'string' },
2127
+ to: { type: 'string' },
2128
+ type: { type: 'string', multiple: true },
2129
+ types: { type: 'string', multiple: true },
2130
+ scope: { type: 'string' },
2131
+ 'out-dir': { type: 'string' },
2132
+ commit: { type: 'boolean' },
2133
+ 'dry-run': { type: 'boolean' },
2134
+ },
2135
+ }));
2136
+ }
2137
+ catch (error) {
2138
+ throw new CliError(String(error), {
2139
+ code: 'INVALID_ARGS',
2140
+ exitCode: 2,
2141
+ });
2142
+ }
2143
+ if (values.commit && values['dry-run']) {
2144
+ throw new CliError('Cannot pass both --commit and --dry-run.', {
2145
+ code: 'DATASET_REFERENCES_REWRITE_MODE_CONFLICT',
2146
+ exitCode: 2,
2147
+ });
2148
+ }
2149
+ return {
2150
+ help: Boolean(values.help),
2151
+ json: Boolean(values.json),
2152
+ inputPath: typeof values.input === 'string' ? values.input : '',
2153
+ from: typeof values.from === 'string' ? values.from : '',
2154
+ to: typeof values.to === 'string' ? values.to : '',
2155
+ types: [
2156
+ ...(Array.isArray(values.type)
2157
+ ? values.type.filter((value) => typeof value === 'string')
2158
+ : []),
2159
+ ...(Array.isArray(values.types)
2160
+ ? values.types.filter((value) => typeof value === 'string')
2161
+ : []),
2162
+ ],
2163
+ scope: typeof values.scope === 'string' ? values.scope : null,
2164
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
2165
+ commit: Boolean(values.commit),
2166
+ };
2167
+ }
2168
+ function parseDatasetReferencesRefreshRemoteFlags(args) {
2169
+ let values;
2170
+ try {
2171
+ ({ values } = parseArgs({
2172
+ args,
2173
+ allowPositionals: false,
2174
+ strict: true,
2175
+ options: {
2176
+ help: { type: 'boolean', short: 'h' },
2177
+ json: { type: 'boolean' },
2178
+ input: { type: 'string' },
2179
+ out: { type: 'string' },
2180
+ 'out-dir': { type: 'string' },
2181
+ 'root-policy': { type: 'string' },
2182
+ },
2183
+ }));
2184
+ }
2185
+ catch (error) {
2186
+ throw new CliError(String(error), {
2187
+ code: 'INVALID_ARGS',
2188
+ exitCode: 2,
2189
+ });
2190
+ }
2191
+ const rawRootPolicy = typeof values['root-policy'] === 'string' ? values['root-policy'] : 'existing';
2192
+ if (!['existing', 'candidate'].includes(rawRootPolicy)) {
2193
+ throw new CliError("--root-policy must be 'existing' or 'candidate'.", {
2194
+ code: 'DATASET_REMOTE_REFRESH_ROOT_POLICY_INVALID',
2195
+ exitCode: 2,
2196
+ });
2197
+ }
2198
+ const rootPolicy = rawRootPolicy;
2199
+ return {
2200
+ help: Boolean(values.help),
2201
+ json: Boolean(values.json),
2202
+ inputPath: typeof values.input === 'string' ? values.input : '',
2203
+ outPath: typeof values.out === 'string' ? values.out : '',
2204
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
2205
+ rootPolicy,
2206
+ };
2207
+ }
2208
+ function parseIdentityPreflightFlags(args) {
2209
+ let values;
2210
+ try {
2211
+ ({ values } = parseArgs({
2212
+ args,
2213
+ allowPositionals: false,
2214
+ strict: true,
2215
+ options: {
2216
+ help: { type: 'boolean', short: 'h' },
2217
+ json: { type: 'boolean' },
2218
+ input: { type: 'string' },
2219
+ 'candidate-input': { type: 'string', multiple: true },
2220
+ 'remote-candidates': { type: 'boolean' },
2221
+ 'remote-query': { type: 'string' },
2222
+ 'remote-limit': { type: 'string' },
2223
+ 'out-dir': { type: 'string' },
2224
+ },
2225
+ }));
2226
+ }
2227
+ catch (error) {
2228
+ throw new CliError(String(error), {
2229
+ code: 'INVALID_ARGS',
2230
+ exitCode: 2,
2231
+ });
2232
+ }
2233
+ const candidateInputValue = values['candidate-input'];
2234
+ const parseRemoteLimit = (value) => {
2235
+ if (typeof value !== 'string') {
2236
+ return null;
2237
+ }
2238
+ const parsed = Number.parseInt(value, 10);
2239
+ if (!Number.isInteger(parsed) || parsed <= 0) {
2240
+ throw new CliError('Expected --remote-limit to be a positive integer.', {
2241
+ code: 'INVALID_IDENTITY_PREFLIGHT_REMOTE_LIMIT',
2242
+ exitCode: 2,
2243
+ });
2244
+ }
2245
+ return parsed;
2246
+ };
2247
+ return {
2248
+ help: Boolean(values.help),
2249
+ json: Boolean(values.json),
2250
+ inputPath: typeof values.input === 'string' ? values.input : '',
1375
2251
  outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
2252
+ candidateInputPaths: Array.isArray(candidateInputValue)
2253
+ ? candidateInputValue.filter((entry) => typeof entry === 'string')
2254
+ : [],
2255
+ remoteCandidateSearch: Boolean(values['remote-candidates']),
2256
+ remoteQuery: typeof values['remote-query'] === 'string' ? values['remote-query'] : null,
2257
+ remoteLimit: parseRemoteLimit(values['remote-limit']),
1376
2258
  };
1377
2259
  }
1378
- function parseDatasetReferencesRewriteFlags(args) {
2260
+ function parseBuildPlanFlags(args) {
1379
2261
  let values;
1380
2262
  try {
1381
2263
  ({ values } = parseArgs({
@@ -1386,14 +2268,8 @@ function parseDatasetReferencesRewriteFlags(args) {
1386
2268
  help: { type: 'boolean', short: 'h' },
1387
2269
  json: { type: 'boolean' },
1388
2270
  input: { type: 'string' },
1389
- from: { type: 'string' },
1390
- to: { type: 'string' },
1391
- type: { type: 'string', multiple: true },
1392
- types: { type: 'string', multiple: true },
1393
- scope: { type: 'string' },
1394
2271
  'out-dir': { type: 'string' },
1395
- commit: { type: 'boolean' },
1396
- 'dry-run': { type: 'boolean' },
2272
+ 'report-only': { type: 'boolean' },
1397
2273
  },
1398
2274
  }));
1399
2275
  }
@@ -1403,29 +2279,12 @@ function parseDatasetReferencesRewriteFlags(args) {
1403
2279
  exitCode: 2,
1404
2280
  });
1405
2281
  }
1406
- if (values.commit && values['dry-run']) {
1407
- throw new CliError('Cannot pass both --commit and --dry-run.', {
1408
- code: 'DATASET_REFERENCES_REWRITE_MODE_CONFLICT',
1409
- exitCode: 2,
1410
- });
1411
- }
1412
2282
  return {
1413
2283
  help: Boolean(values.help),
1414
2284
  json: Boolean(values.json),
1415
2285
  inputPath: typeof values.input === 'string' ? values.input : '',
1416
- from: typeof values.from === 'string' ? values.from : '',
1417
- to: typeof values.to === 'string' ? values.to : '',
1418
- types: [
1419
- ...(Array.isArray(values.type)
1420
- ? values.type.filter((value) => typeof value === 'string')
1421
- : []),
1422
- ...(Array.isArray(values.types)
1423
- ? values.types.filter((value) => typeof value === 'string')
1424
- : []),
1425
- ],
1426
- scope: typeof values.scope === 'string' ? values.scope : null,
1427
- outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : '',
1428
- commit: Boolean(values.commit),
2286
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
2287
+ reportOnly: Boolean(values['report-only']),
1429
2288
  };
1430
2289
  }
1431
2290
  function parseFlowRemediateFlags(args) {
@@ -2830,6 +3689,40 @@ function parseProcessSaveDraftFlags(args) {
2830
3689
  commit: Boolean(values.commit),
2831
3690
  };
2832
3691
  }
3692
+ function parseProcessRequiredFieldsFlags(args) {
3693
+ let values;
3694
+ try {
3695
+ ({ values } = parseArgs({
3696
+ args,
3697
+ allowPositionals: false,
3698
+ strict: true,
3699
+ options: {
3700
+ help: { type: 'boolean', short: 'h' },
3701
+ json: { type: 'boolean' },
3702
+ input: { type: 'string' },
3703
+ out: { type: 'string' },
3704
+ 'out-dir': { type: 'string' },
3705
+ flows: { type: 'string' },
3706
+ 'default-unit': { type: 'string' },
3707
+ },
3708
+ }));
3709
+ }
3710
+ catch (error) {
3711
+ throw new CliError(String(error), {
3712
+ code: 'INVALID_ARGS',
3713
+ exitCode: 2,
3714
+ });
3715
+ }
3716
+ return {
3717
+ help: Boolean(values.help),
3718
+ json: Boolean(values.json),
3719
+ inputPath: typeof values.input === 'string' ? values.input : '',
3720
+ outPath: typeof values.out === 'string' ? values.out : '',
3721
+ outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : null,
3722
+ flowInputPath: typeof values.flows === 'string' ? values.flows : null,
3723
+ defaultUnit: typeof values['default-unit'] === 'string' ? values['default-unit'] : null,
3724
+ };
3725
+ }
2833
3726
  function parseProcessRefreshReferencesFlags(args) {
2834
3727
  let values;
2835
3728
  try {
@@ -2982,7 +3875,11 @@ export async function executeCli(argv, deps) {
2982
3875
  const processResumeBuildImpl = deps.runProcessResumeBuildImpl ?? runProcessResumeBuild;
2983
3876
  const processPublishBuildImpl = deps.runProcessPublishBuildImpl ?? runProcessPublishBuild;
2984
3877
  const processSaveDraftImpl = deps.runProcessSaveDraftImpl ?? runProcessSaveDraft;
3878
+ const processRequiredFieldsCompleteImpl = deps.runProcessRequiredFieldsCompleteImpl ?? runProcessRequiredFieldsComplete;
2985
3879
  const processVerifyRowsImpl = deps.runProcessVerifyRowsImpl ?? runProcessVerifyRows;
3880
+ const processIdentityPreflightImpl = deps.runProcessIdentityPreflightImpl ?? runProcessIdentityPreflight;
3881
+ const processBuildPlanValidateImpl = deps.runProcessBuildPlanValidateImpl ?? runProcessBuildPlanValidate;
3882
+ const processBuildPlanMaterializeImpl = deps.runProcessBuildPlanMaterializeImpl ?? runProcessBuildPlanMaterialize;
2986
3883
  const processReviewImpl = deps.runProcessReviewImpl ?? runProcessReview;
2987
3884
  const flowReviewImpl = deps.runFlowReviewImpl ?? runFlowReview;
2988
3885
  const lifecyclemodelReviewImpl = deps.runLifecyclemodelReviewImpl ?? runLifecyclemodelReview;
@@ -2999,8 +3896,20 @@ export async function executeCli(argv, deps) {
2999
3896
  const flowApplyProcessFlowRepairsImpl = deps.runFlowApplyProcessFlowRepairsImpl ?? runFlowApplyProcessFlowRepairs;
3000
3897
  const flowRegenProductImpl = deps.runFlowRegenProductImpl ?? runFlowRegenProduct;
3001
3898
  const flowValidateProcessesImpl = deps.runFlowValidateProcessesImpl ?? runFlowValidateProcesses;
3899
+ const flowIdentityPreflightImpl = deps.runFlowIdentityPreflightImpl ?? runFlowIdentityPreflight;
3900
+ const flowBuildPlanValidateImpl = deps.runFlowBuildPlanValidateImpl ?? runFlowBuildPlanValidate;
3901
+ const flowBuildPlanMaterializeImpl = deps.runFlowBuildPlanMaterializeImpl ?? runFlowBuildPlanMaterialize;
3002
3902
  const datasetValidateImpl = deps.runDatasetValidateImpl ?? runDatasetValidate;
3003
3903
  const datasetReferencesRewriteImpl = deps.runDatasetReferencesRewriteImpl ?? runDatasetReferencesRewrite;
3904
+ const datasetRemoteRefreshImpl = deps.runDatasetRemoteRefreshImpl ?? runDatasetRemoteRefresh;
3905
+ const datasetRemoteVerifyImpl = deps.runDatasetRemoteVerifyImpl ?? runDatasetRemoteVerify;
3906
+ const datasetBilingualExtractImpl = deps.runDatasetBilingualExtractImpl ?? runDatasetBilingualExtract;
3907
+ const datasetBilingualApplyImpl = deps.runDatasetBilingualApplyImpl ?? runDatasetBilingualApply;
3908
+ const datasetBilingualValidateImpl = deps.runDatasetBilingualValidateImpl ?? runDatasetBilingualValidate;
3909
+ const datasetEvidenceSearchImpl = deps.runDatasetEvidenceSearchImpl ?? runDatasetEvidenceSearch;
3910
+ const datasetContractImpl = deps.runDatasetContractImpl ?? runDatasetContract;
3911
+ const datasetImportLcaConvertImpl = deps.runDatasetImportLcaConvertImpl ?? runDatasetImportLcaConvert;
3912
+ const datasetAuthorImpl = deps.runDatasetAuthorImpl ?? runDatasetAuthor;
3004
3913
  if (flags.version) {
3005
3914
  return { exitCode: 0, stdout: `${loadCliPackageVersion(import.meta.url)}\n`, stderr: '' };
3006
3915
  }
@@ -3046,6 +3955,110 @@ export async function executeCli(argv, deps) {
3046
3955
  if (command === 'dataset' && !subcommand) {
3047
3956
  return { exitCode: 0, stdout: `${renderDatasetHelp()}\n`, stderr: '' };
3048
3957
  }
3958
+ if (command === 'dataset' && subcommand === 'contract') {
3959
+ const action = commandArgs[0] ?? '';
3960
+ if (!action || action === '--help' || action === '-h') {
3961
+ return { exitCode: 0, stdout: `${renderDatasetContractHelp()}\n`, stderr: '' };
3962
+ }
3963
+ if (action !== 'get') {
3964
+ throw new CliError("dataset contract action must be 'get'.", {
3965
+ code: 'DATASET_CONTRACT_ACTION_INVALID',
3966
+ exitCode: 2,
3967
+ });
3968
+ }
3969
+ const datasetFlags = parseDatasetContractFlags(commandArgs.slice(1));
3970
+ if (datasetFlags.help) {
3971
+ return { exitCode: 0, stdout: `${renderDatasetContractHelp()}\n`, stderr: '' };
3972
+ }
3973
+ const report = await datasetContractImpl({
3974
+ type: datasetFlags.type,
3975
+ include: datasetFlags.include,
3976
+ profile: datasetFlags.profile,
3977
+ outDir: datasetFlags.outDir,
3978
+ mode: 'contract',
3979
+ });
3980
+ return {
3981
+ exitCode: 0,
3982
+ stdout: stringifyJson(report, datasetFlags.json),
3983
+ stderr: '',
3984
+ };
3985
+ }
3986
+ if (command === 'dataset' && subcommand === 'context-pack') {
3987
+ const datasetFlags = parseDatasetContractFlags(commandArgs);
3988
+ if (datasetFlags.help) {
3989
+ return { exitCode: 0, stdout: `${renderDatasetContextPackHelp()}\n`, stderr: '' };
3990
+ }
3991
+ const report = await datasetContractImpl({
3992
+ type: datasetFlags.type,
3993
+ include: datasetFlags.include,
3994
+ profile: datasetFlags.profile,
3995
+ outDir: datasetFlags.outDir,
3996
+ mode: 'context-pack',
3997
+ });
3998
+ return {
3999
+ exitCode: 0,
4000
+ stdout: stringifyJson(report, datasetFlags.json),
4001
+ stderr: '',
4002
+ };
4003
+ }
4004
+ if (command === 'dataset' && subcommand === 'import-lca') {
4005
+ const action = commandArgs[0] ?? '';
4006
+ if (!action || action === '--help' || action === '-h') {
4007
+ return { exitCode: 0, stdout: `${renderDatasetImportLcaHelp()}\n`, stderr: '' };
4008
+ }
4009
+ if (action !== 'convert') {
4010
+ throw new CliError("dataset import-lca action must be 'convert'.", {
4011
+ code: 'DATASET_IMPORT_LCA_ACTION_INVALID',
4012
+ exitCode: 2,
4013
+ });
4014
+ }
4015
+ const datasetFlags = parseDatasetImportLcaConvertFlags(commandArgs.slice(1));
4016
+ if (datasetFlags.help) {
4017
+ return { exitCode: 0, stdout: `${renderDatasetImportLcaHelp()}\n`, stderr: '' };
4018
+ }
4019
+ const report = await datasetImportLcaConvertImpl({
4020
+ inputPath: datasetFlags.inputPath,
4021
+ outputDir: datasetFlags.outputDir,
4022
+ fromFormat: datasetFlags.fromFormat,
4023
+ target: datasetFlags.target,
4024
+ reportPath: datasetFlags.reportPath,
4025
+ mappingDir: datasetFlags.mappingDir,
4026
+ language: datasetFlags.language,
4027
+ validationJobs: datasetFlags.validationJobs,
4028
+ detectOnly: datasetFlags.detectOnly,
4029
+ failOnWarning: datasetFlags.failOnWarning,
4030
+ pythonBin: datasetFlags.pythonBin,
4031
+ tidasToolsDir: datasetFlags.tidasToolsDir,
4032
+ env: deps.env,
4033
+ });
4034
+ return {
4035
+ exitCode: report.status === 'completed' ? 0 : 1,
4036
+ stdout: stringifyJson(report, datasetFlags.json),
4037
+ stderr: '',
4038
+ };
4039
+ }
4040
+ if (command === 'dataset' && subcommand === 'author') {
4041
+ const datasetFlags = parseDatasetAuthorFlags(commandArgs);
4042
+ if (datasetFlags.help) {
4043
+ return { exitCode: 0, stdout: `${renderDatasetAuthorHelp()}\n`, stderr: '' };
4044
+ }
4045
+ const report = await datasetAuthorImpl({
4046
+ inputPath: datasetFlags.inputPath,
4047
+ targetTypes: datasetFlags.targetTypes,
4048
+ outDir: datasetFlags.outDir,
4049
+ prompt: datasetFlags.prompt,
4050
+ provider: datasetFlags.provider,
4051
+ model: datasetFlags.model,
4052
+ timeoutMs: datasetFlags.timeoutMs,
4053
+ env: deps.env,
4054
+ fetchImpl: deps.fetchImpl,
4055
+ });
4056
+ return {
4057
+ exitCode: 0,
4058
+ stdout: stringifyJson(report, datasetFlags.json),
4059
+ stderr: '',
4060
+ };
4061
+ }
3049
4062
  if (command === 'dataset' && subcommand === 'validate') {
3050
4063
  const datasetFlags = parseDatasetValidateFlags(commandArgs);
3051
4064
  if (datasetFlags.help) {
@@ -3062,17 +4075,151 @@ export async function executeCli(argv, deps) {
3062
4075
  stderr: '',
3063
4076
  };
3064
4077
  }
4078
+ if (command === 'dataset' && subcommand === 'verify-remote') {
4079
+ const datasetFlags = parseDatasetRemoteVerifyFlags(commandArgs);
4080
+ if (datasetFlags.help) {
4081
+ return { exitCode: 0, stdout: `${renderDatasetRemoteVerifyHelp()}\n`, stderr: '' };
4082
+ }
4083
+ const report = await datasetRemoteVerifyImpl({
4084
+ inputPath: datasetFlags.inputPath,
4085
+ outDir: datasetFlags.outDir,
4086
+ rootPolicy: datasetFlags.rootPolicy,
4087
+ env: deps.env,
4088
+ fetchImpl: deps.fetchImpl,
4089
+ });
4090
+ return {
4091
+ exitCode: report.status === 'blocked_remote_verification' ? 1 : 0,
4092
+ stdout: stringifyJson(report, datasetFlags.json),
4093
+ stderr: '',
4094
+ };
4095
+ }
4096
+ if (command === 'dataset' && subcommand === 'bilingual') {
4097
+ const action = commandArgs[0] ?? '';
4098
+ if (!action || action === '--help' || action === '-h') {
4099
+ return { exitCode: 0, stdout: `${renderDatasetBilingualHelp()}\n`, stderr: '' };
4100
+ }
4101
+ if (action === 'extract') {
4102
+ const datasetFlags = parseDatasetBilingualExtractFlags(commandArgs.slice(1));
4103
+ if (datasetFlags.help) {
4104
+ return { exitCode: 0, stdout: `${renderDatasetBilingualExtractHelp()}\n`, stderr: '' };
4105
+ }
4106
+ const report = await datasetBilingualExtractImpl({
4107
+ inputPath: datasetFlags.inputPath,
4108
+ type: datasetFlags.type,
4109
+ sourceLang: datasetFlags.sourceLang,
4110
+ targetLang: datasetFlags.targetLang,
4111
+ outDir: datasetFlags.outDir,
4112
+ });
4113
+ return {
4114
+ exitCode: 0,
4115
+ stdout: stringifyJson(report, datasetFlags.json),
4116
+ stderr: '',
4117
+ };
4118
+ }
4119
+ if (action === 'apply') {
4120
+ const datasetFlags = parseDatasetBilingualApplyFlags(commandArgs.slice(1));
4121
+ if (datasetFlags.help) {
4122
+ return { exitCode: 0, stdout: `${renderDatasetBilingualApplyHelp()}\n`, stderr: '' };
4123
+ }
4124
+ const report = await datasetBilingualApplyImpl({
4125
+ inputPath: datasetFlags.inputPath,
4126
+ translationsPath: datasetFlags.translationsPath,
4127
+ outPath: datasetFlags.outPath,
4128
+ targetLang: datasetFlags.targetLang,
4129
+ outDir: datasetFlags.outDir,
4130
+ });
4131
+ return {
4132
+ exitCode: report.status === 'blocked' ? 1 : 0,
4133
+ stdout: stringifyJson(report, datasetFlags.json),
4134
+ stderr: '',
4135
+ };
4136
+ }
4137
+ if (action === 'validate') {
4138
+ const datasetFlags = parseDatasetBilingualValidateFlags(commandArgs.slice(1));
4139
+ if (datasetFlags.help) {
4140
+ return { exitCode: 0, stdout: `${renderDatasetBilingualValidateHelp()}\n`, stderr: '' };
4141
+ }
4142
+ const report = await datasetBilingualValidateImpl({
4143
+ inputPath: datasetFlags.inputPath,
4144
+ type: datasetFlags.type,
4145
+ outDir: datasetFlags.outDir,
4146
+ });
4147
+ return {
4148
+ exitCode: report.status === 'blocked' ? 1 : 0,
4149
+ stdout: stringifyJson(report, datasetFlags.json),
4150
+ stderr: '',
4151
+ };
4152
+ }
4153
+ throw new CliError("dataset bilingual action must be 'extract', 'apply', or 'validate'.", {
4154
+ code: 'INVALID_ARGS',
4155
+ exitCode: 2,
4156
+ });
4157
+ }
4158
+ if (command === 'dataset' && subcommand === 'evidence-search') {
4159
+ const action = commandArgs[0] ?? '';
4160
+ if (!action || action === '--help' || action === '-h') {
4161
+ return { exitCode: 0, stdout: `${renderDatasetEvidenceSearchHelp()}\n`, stderr: '' };
4162
+ }
4163
+ if (action !== 'plan' && action !== 'run') {
4164
+ throw new CliError("dataset evidence-search action must be 'plan' or 'run'.", {
4165
+ code: 'INVALID_ARGS',
4166
+ exitCode: 2,
4167
+ });
4168
+ }
4169
+ const datasetFlags = parseDatasetEvidenceSearchFlags(commandArgs.slice(1));
4170
+ if (datasetFlags.help) {
4171
+ return { exitCode: 0, stdout: `${renderDatasetEvidenceSearchHelp()}\n`, stderr: '' };
4172
+ }
4173
+ const report = await datasetEvidenceSearchImpl({
4174
+ mode: action,
4175
+ query: datasetFlags.query,
4176
+ inputPath: datasetFlags.inputPath,
4177
+ resultsPath: datasetFlags.resultsPath,
4178
+ providerUrl: datasetFlags.providerUrl,
4179
+ providerKey: datasetFlags.providerKey,
4180
+ profile: datasetFlags.profile,
4181
+ outDir: datasetFlags.outDir,
4182
+ maxQueries: datasetFlags.maxQueries,
4183
+ maxResultsPerQuery: datasetFlags.maxResultsPerQuery,
4184
+ timeoutMs: datasetFlags.timeoutMs,
4185
+ fetchImpl: deps.fetchImpl,
4186
+ });
4187
+ return {
4188
+ exitCode: report.status === 'completed_no_sufficient_evidence' ? 1 : 0,
4189
+ stdout: stringifyJson(report, datasetFlags.json),
4190
+ stderr: '',
4191
+ };
4192
+ }
3065
4193
  if (command === 'dataset' && subcommand === 'references') {
3066
4194
  const action = commandArgs[0] ?? '';
3067
4195
  if (!action || action === '--help' || action === '-h') {
3068
4196
  return { exitCode: 0, stdout: `${renderDatasetReferencesHelp()}\n`, stderr: '' };
3069
4197
  }
3070
- if (action !== 'rewrite') {
3071
- throw new CliError("dataset references action must be 'rewrite'.", {
4198
+ if (!['rewrite', 'refresh-remote'].includes(action)) {
4199
+ throw new CliError("dataset references action must be 'rewrite' or 'refresh-remote'.", {
3072
4200
  code: 'INVALID_ARGS',
3073
4201
  exitCode: 2,
3074
4202
  });
3075
4203
  }
4204
+ if (action === 'refresh-remote') {
4205
+ const datasetFlags = parseDatasetReferencesRefreshRemoteFlags(commandArgs.slice(1));
4206
+ if (datasetFlags.help) {
4207
+ return { exitCode: 0, stdout: `${renderDatasetReferencesHelp()}\n`, stderr: '' };
4208
+ }
4209
+ const report = await datasetRemoteRefreshImpl({
4210
+ inputPath: datasetFlags.inputPath,
4211
+ outPath: datasetFlags.outPath,
4212
+ outDir: datasetFlags.outDir,
4213
+ rootPolicy: datasetFlags.rootPolicy,
4214
+ env: deps.env,
4215
+ fetchImpl: deps.fetchImpl,
4216
+ });
4217
+ return {
4218
+ exitCode: report.status === 'completed_with_blockers' ? 1 : 0,
4219
+ stdout: stringifyJson(report, datasetFlags.json),
4220
+ stderr: '',
4221
+ };
4222
+ }
3076
4223
  const datasetFlags = parseDatasetReferencesRewriteFlags(commandArgs.slice(1));
3077
4224
  if (datasetFlags.help) {
3078
4225
  return { exitCode: 0, stdout: `${renderDatasetReferencesHelp()}\n`, stderr: '' };
@@ -3327,6 +4474,71 @@ export async function executeCli(argv, deps) {
3327
4474
  stderr: '',
3328
4475
  };
3329
4476
  }
4477
+ if (command === 'process' && subcommand === 'identity-preflight') {
4478
+ const processFlags = parseIdentityPreflightFlags(commandArgs);
4479
+ if (processFlags.help) {
4480
+ return {
4481
+ exitCode: 0,
4482
+ stdout: `${renderProcessIdentityPreflightHelp()}\n`,
4483
+ stderr: '',
4484
+ };
4485
+ }
4486
+ const report = await processIdentityPreflightImpl({
4487
+ inputPath: processFlags.inputPath,
4488
+ outDir: processFlags.outDir,
4489
+ candidateInputPaths: processFlags.candidateInputPaths,
4490
+ remoteCandidateSearch: processFlags.remoteCandidateSearch,
4491
+ remoteQuery: processFlags.remoteQuery,
4492
+ remoteLimit: processFlags.remoteLimit,
4493
+ env: deps.env,
4494
+ fetchImpl: deps.fetchImpl,
4495
+ });
4496
+ return {
4497
+ exitCode: report.status === 'passed' ? 0 : 1,
4498
+ stdout: stringifyJson(report, processFlags.json),
4499
+ stderr: '',
4500
+ };
4501
+ }
4502
+ if (command === 'process' && subcommand === 'build-plan') {
4503
+ const action = commandArgs[0] ?? '';
4504
+ if (!action || action === '--help' || action === '-h') {
4505
+ return {
4506
+ exitCode: 0,
4507
+ stdout: `${renderProcessBuildPlanHelp()}\n`,
4508
+ stderr: '',
4509
+ };
4510
+ }
4511
+ if (action !== 'validate' && action !== 'materialize') {
4512
+ throw new CliError("process build-plan action must be 'validate' or 'materialize'.", {
4513
+ code: 'INVALID_ARGS',
4514
+ exitCode: 2,
4515
+ });
4516
+ }
4517
+ const processFlags = parseBuildPlanFlags(commandArgs.slice(1));
4518
+ if (processFlags.help) {
4519
+ return {
4520
+ exitCode: 0,
4521
+ stdout: `${renderProcessBuildPlanHelp()}\n`,
4522
+ stderr: '',
4523
+ };
4524
+ }
4525
+ const report = action === 'validate'
4526
+ ? await processBuildPlanValidateImpl({
4527
+ inputPath: processFlags.inputPath,
4528
+ outDir: processFlags.outDir,
4529
+ reportOnly: processFlags.reportOnly,
4530
+ })
4531
+ : await processBuildPlanMaterializeImpl({
4532
+ inputPath: processFlags.inputPath,
4533
+ outDir: processFlags.outDir,
4534
+ reportOnly: processFlags.reportOnly,
4535
+ });
4536
+ return {
4537
+ exitCode: report.status === 'blocked' && !processFlags.reportOnly ? 1 : 0,
4538
+ stdout: stringifyJson(report, processFlags.json),
4539
+ stderr: '',
4540
+ };
4541
+ }
3330
4542
  if (command === 'process' && subcommand === 'scope-statistics') {
3331
4543
  const processFlags = parseProcessScopeStatisticsFlags(commandArgs);
3332
4544
  if (processFlags.help) {
@@ -3430,6 +4642,28 @@ export async function executeCli(argv, deps) {
3430
4642
  stderr: '',
3431
4643
  };
3432
4644
  }
4645
+ if (command === 'process' && subcommand === 'complete-required-fields') {
4646
+ const processFlags = parseProcessRequiredFieldsFlags(commandArgs);
4647
+ if (processFlags.help) {
4648
+ return {
4649
+ exitCode: 0,
4650
+ stdout: `${renderProcessRequiredFieldsHelp()}\n`,
4651
+ stderr: '',
4652
+ };
4653
+ }
4654
+ const report = await processRequiredFieldsCompleteImpl({
4655
+ inputPath: processFlags.inputPath,
4656
+ outPath: processFlags.outPath,
4657
+ outDir: processFlags.outDir,
4658
+ flowInputPath: processFlags.flowInputPath,
4659
+ defaultUnit: processFlags.defaultUnit,
4660
+ });
4661
+ return {
4662
+ exitCode: report.status === 'completed_with_blockers' ? 1 : 0,
4663
+ stdout: stringifyJson(report, processFlags.json),
4664
+ stderr: '',
4665
+ };
4666
+ }
3433
4667
  if (command === 'process' && subcommand === 'save-draft') {
3434
4668
  const processFlags = parseProcessSaveDraftFlags(commandArgs);
3435
4669
  if (processFlags.help) {
@@ -3562,6 +4796,59 @@ export async function executeCli(argv, deps) {
3562
4796
  stderr: '',
3563
4797
  };
3564
4798
  }
4799
+ if (command === 'flow' && subcommand === 'identity-preflight') {
4800
+ const flowFlags = parseIdentityPreflightFlags(commandArgs);
4801
+ if (flowFlags.help) {
4802
+ return { exitCode: 0, stdout: `${renderFlowIdentityPreflightHelp()}\n`, stderr: '' };
4803
+ }
4804
+ const report = await flowIdentityPreflightImpl({
4805
+ inputPath: flowFlags.inputPath,
4806
+ outDir: flowFlags.outDir,
4807
+ candidateInputPaths: flowFlags.candidateInputPaths,
4808
+ remoteCandidateSearch: flowFlags.remoteCandidateSearch,
4809
+ remoteQuery: flowFlags.remoteQuery,
4810
+ remoteLimit: flowFlags.remoteLimit,
4811
+ env: deps.env,
4812
+ fetchImpl: deps.fetchImpl,
4813
+ });
4814
+ return {
4815
+ exitCode: report.status === 'passed' ? 0 : 1,
4816
+ stdout: stringifyJson(report, flowFlags.json),
4817
+ stderr: '',
4818
+ };
4819
+ }
4820
+ if (command === 'flow' && subcommand === 'build-plan') {
4821
+ const action = commandArgs[0] ?? '';
4822
+ if (!action || action === '--help' || action === '-h') {
4823
+ return { exitCode: 0, stdout: `${renderFlowBuildPlanHelp()}\n`, stderr: '' };
4824
+ }
4825
+ if (action !== 'validate' && action !== 'materialize') {
4826
+ throw new CliError("flow build-plan action must be 'validate' or 'materialize'.", {
4827
+ code: 'INVALID_ARGS',
4828
+ exitCode: 2,
4829
+ });
4830
+ }
4831
+ const flowFlags = parseBuildPlanFlags(commandArgs.slice(1));
4832
+ if (flowFlags.help) {
4833
+ return { exitCode: 0, stdout: `${renderFlowBuildPlanHelp()}\n`, stderr: '' };
4834
+ }
4835
+ const report = action === 'validate'
4836
+ ? await flowBuildPlanValidateImpl({
4837
+ inputPath: flowFlags.inputPath,
4838
+ outDir: flowFlags.outDir,
4839
+ reportOnly: flowFlags.reportOnly,
4840
+ })
4841
+ : await flowBuildPlanMaterializeImpl({
4842
+ inputPath: flowFlags.inputPath,
4843
+ outDir: flowFlags.outDir,
4844
+ reportOnly: flowFlags.reportOnly,
4845
+ });
4846
+ return {
4847
+ exitCode: report.status === 'blocked' && !flowFlags.reportOnly ? 1 : 0,
4848
+ stdout: stringifyJson(report, flowFlags.json),
4849
+ stderr: '',
4850
+ };
4851
+ }
3565
4852
  if (command === 'flow' && subcommand === 'remediate') {
3566
4853
  const flowFlags = parseFlowRemediateFlags(commandArgs);
3567
4854
  if (flowFlags.help) {