@lingjingai/scriptctl 0.49.16 → 0.51.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/README.md +8 -2
- package/changes/0.50.0.md +63 -0
- package/changes/0.51.0.md +24 -0
- package/dist/cli/register-batch.d.ts +3 -0
- package/dist/cli/register-batch.js +28 -0
- package/dist/cli/register-batch.js.map +1 -0
- package/dist/cli/register-edits.d.ts +4 -0
- package/dist/cli/register-edits.js +226 -0
- package/dist/cli/register-edits.js.map +1 -0
- package/dist/cli/register-query.d.ts +4 -0
- package/dist/cli/register-query.js +93 -0
- package/dist/cli/register-query.js.map +1 -0
- package/dist/cli/register-tools.d.ts +3 -0
- package/dist/cli/register-tools.js +23 -0
- package/dist/cli/register-tools.js.map +1 -0
- package/dist/cli/register-workflows.d.ts +4 -0
- package/dist/cli/register-workflows.js +71 -0
- package/dist/cli/register-workflows.js.map +1 -0
- package/dist/cli/registry.d.ts +14 -0
- package/dist/cli/registry.js +65 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +146 -575
- package/dist/cli.js.map +1 -1
- package/dist/common.d.ts +12 -38
- package/dist/common.js +5 -11
- package/dist/common.js.map +1 -1
- package/dist/domain/authoring/apply.d.ts +1 -33
- package/dist/domain/authoring/apply.js +1 -783
- package/dist/domain/authoring/apply.js.map +1 -1
- package/dist/domain/authoring/export.d.ts +1 -7
- package/dist/domain/authoring/export.js +1 -84
- package/dist/domain/authoring/export.js.map +1 -1
- package/dist/domain/authoring/parser.d.ts +2 -3
- package/dist/domain/authoring/parser.js +2 -914
- package/dist/domain/authoring/parser.js.map +1 -1
- package/dist/domain/authoring/serializer.d.ts +1 -2
- package/dist/domain/authoring/serializer.js +1 -243
- package/dist/domain/authoring/serializer.js.map +1 -1
- package/dist/domain/authoring/shared/bracket.d.ts +4 -0
- package/dist/domain/authoring/shared/bracket.js +95 -0
- package/dist/domain/authoring/shared/bracket.js.map +1 -0
- package/dist/domain/authoring/shared/types.d.ts +12 -0
- package/dist/domain/authoring/shared/types.js +2 -0
- package/dist/domain/authoring/shared/types.js.map +1 -0
- package/dist/domain/authoring/types.d.ts +2 -87
- package/dist/domain/authoring/types.js +2 -1
- package/dist/domain/authoring/types.js.map +1 -1
- package/dist/domain/authoring/v3/apply.d.ts +33 -0
- package/dist/domain/authoring/v3/apply.js +784 -0
- package/dist/domain/authoring/v3/apply.js.map +1 -0
- package/dist/domain/authoring/v3/export.d.ts +7 -0
- package/dist/domain/authoring/v3/export.js +85 -0
- package/dist/domain/authoring/v3/export.js.map +1 -0
- package/dist/domain/authoring/v3/parser.d.ts +3 -0
- package/dist/domain/authoring/v3/parser.js +823 -0
- package/dist/domain/authoring/v3/parser.js.map +1 -0
- package/dist/domain/authoring/v3/serializer.d.ts +2 -0
- package/dist/domain/authoring/v3/serializer.js +244 -0
- package/dist/domain/authoring/v3/serializer.js.map +1 -0
- package/dist/domain/authoring/v3/types.d.ts +76 -0
- package/dist/domain/authoring/v3/types.js +2 -0
- package/dist/domain/authoring/v3/types.js.map +1 -0
- package/dist/domain/authoring/v4/apply.d.ts +40 -0
- package/dist/domain/authoring/v4/apply.js +451 -0
- package/dist/domain/authoring/v4/apply.js.map +1 -0
- package/dist/domain/authoring/v4/parser.d.ts +2 -0
- package/dist/domain/authoring/v4/parser.js +729 -0
- package/dist/domain/authoring/v4/parser.js.map +1 -0
- package/dist/domain/authoring/v4/serializer.d.ts +6 -0
- package/dist/domain/authoring/v4/serializer.js +280 -0
- package/dist/domain/authoring/v4/serializer.js.map +1 -0
- package/dist/domain/authoring/v4/types.d.ts +50 -0
- package/dist/domain/authoring/v4/types.js +2 -0
- package/dist/domain/authoring/v4/types.js.map +1 -0
- package/dist/domain/ingest/apply-plan.d.ts +1 -1
- package/dist/domain/ingest/assembler.d.ts +9 -0
- package/dist/domain/ingest/assembler.js +98 -36
- package/dist/domain/ingest/assembler.js.map +1 -1
- package/dist/domain/ingest/asset-audit.d.ts +50 -0
- package/dist/domain/ingest/asset-audit.js +196 -0
- package/dist/domain/ingest/asset-audit.js.map +1 -0
- package/dist/domain/ingest/production-validation.d.ts +5 -4
- package/dist/domain/ingest/production-validation.js +10 -25
- package/dist/domain/ingest/production-validation.js.map +1 -1
- package/dist/domain/ingest/records.d.ts +1 -1
- package/dist/domain/ingest/records.js +1 -1
- package/dist/domain/ingest/records.js.map +1 -1
- package/dist/domain/ingest/serial-context.d.ts +1 -1
- package/dist/domain/ingest/validation.d.ts +1 -11
- package/dist/domain/ingest/validation.js +1 -199
- package/dist/domain/ingest/validation.js.map +1 -1
- package/dist/domain/ingest/video-apply.js +68 -45
- package/dist/domain/ingest/video-apply.js.map +1 -1
- package/dist/domain/script/address.d.ts +1 -32
- package/dist/domain/script/address.js +1 -144
- package/dist/domain/script/address.js.map +1 -1
- package/dist/domain/script/dispatch.d.ts +14 -0
- package/dist/domain/script/dispatch.js +45 -0
- package/dist/domain/script/dispatch.js.map +1 -0
- package/dist/domain/script/lookups.d.ts +1 -33
- package/dist/domain/script/lookups.js +1 -316
- package/dist/domain/script/lookups.js.map +1 -1
- package/dist/domain/script/parse.d.ts +1 -3
- package/dist/domain/script/parse.js +1 -164
- package/dist/domain/script/parse.js.map +1 -1
- package/dist/domain/script/patch/apply.d.ts +1 -2
- package/dist/domain/script/patch/apply.js +1 -30
- package/dist/domain/script/patch/apply.js.map +1 -1
- package/dist/domain/script/patch/helpers.d.ts +1 -17
- package/dist/domain/script/patch/helpers.js +1 -210
- package/dist/domain/script/patch/helpers.js.map +1 -1
- package/dist/domain/script/patch/inline-speaker.d.ts +1 -2
- package/dist/domain/script/patch/inline-speaker.js +1 -49
- package/dist/domain/script/patch/inline-speaker.js.map +1 -1
- package/dist/domain/script/patch/ops-action.d.ts +1 -12
- package/dist/domain/script/patch/ops-action.js +1 -366
- package/dist/domain/script/patch/ops-action.js.map +1 -1
- package/dist/domain/script/patch/ops-asset.d.ts +1 -14
- package/dist/domain/script/patch/ops-asset.js +1 -364
- package/dist/domain/script/patch/ops-asset.js.map +1 -1
- package/dist/domain/script/patch/ops-dialogue.d.ts +1 -2
- package/dist/domain/script/patch/ops-dialogue.js +1 -25
- package/dist/domain/script/patch/ops-dialogue.js.map +1 -1
- package/dist/domain/script/patch/ops-extend.d.ts +1 -8
- package/dist/domain/script/patch/ops-extend.js +1 -110
- package/dist/domain/script/patch/ops-extend.js.map +1 -1
- package/dist/domain/script/patch/ops-meta.d.ts +1 -5
- package/dist/domain/script/patch/ops-meta.js +1 -81
- package/dist/domain/script/patch/ops-meta.js.map +1 -1
- package/dist/domain/script/patch/ops-scene.d.ts +1 -6
- package/dist/domain/script/patch/ops-scene.js +1 -350
- package/dist/domain/script/patch/ops-scene.js.map +1 -1
- package/dist/domain/script/patch/ops-state.d.ts +1 -8
- package/dist/domain/script/patch/ops-state.js +1 -164
- package/dist/domain/script/patch/ops-state.js.map +1 -1
- package/dist/domain/script/patch/registry.d.ts +1 -2
- package/dist/domain/script/patch/registry.js +1 -52
- package/dist/domain/script/patch/registry.js.map +1 -1
- package/dist/domain/script/patch/types.d.ts +1 -10
- package/dist/domain/script/patch/types.js +1 -1
- package/dist/domain/script/patch/types.js.map +1 -1
- package/dist/domain/script/refs.d.ts +1 -4
- package/dist/domain/script/refs.js +1 -63
- package/dist/domain/script/refs.js.map +1 -1
- package/dist/domain/script/scene-refs.d.ts +1 -3
- package/dist/domain/script/scene-refs.js +1 -34
- package/dist/domain/script/scene-refs.js.map +1 -1
- package/dist/domain/script/schema.d.ts +1 -6
- package/dist/domain/script/schema.js +1 -55
- package/dist/domain/script/schema.js.map +1 -1
- package/dist/domain/script/selector.d.ts +1 -45
- package/dist/domain/script/selector.js +1 -184
- package/dist/domain/script/selector.js.map +1 -1
- package/dist/domain/script/shared/regex.d.ts +2 -0
- package/dist/domain/script/shared/regex.js +7 -0
- package/dist/domain/script/shared/regex.js.map +1 -0
- package/dist/domain/script/shared/types.d.ts +18 -0
- package/dist/domain/script/shared/types.js +9 -0
- package/dist/domain/script/shared/types.js.map +1 -0
- package/dist/domain/script/shared/validation.d.ts +10 -0
- package/dist/domain/script/shared/validation.js +2 -0
- package/dist/domain/script/shared/validation.js.map +1 -0
- package/dist/domain/script/shared.js +4 -6
- package/dist/domain/script/shared.js.map +1 -1
- package/dist/domain/script/types.d.ts +1 -123
- package/dist/domain/script/types.js +1 -8
- package/dist/domain/script/types.js.map +1 -1
- package/dist/domain/script/v3/address.d.ts +32 -0
- package/dist/domain/script/v3/address.js +136 -0
- package/dist/domain/script/v3/address.js.map +1 -0
- package/dist/domain/script/v3/lookups.d.ts +33 -0
- package/dist/domain/script/v3/lookups.js +316 -0
- package/dist/domain/script/v3/lookups.js.map +1 -0
- package/dist/domain/script/v3/ordinal.d.ts +8 -0
- package/dist/domain/script/v3/ordinal.js +91 -0
- package/dist/domain/script/v3/ordinal.js.map +1 -0
- package/dist/domain/script/v3/parse.d.ts +3 -0
- package/dist/domain/script/v3/parse.js +164 -0
- package/dist/domain/script/v3/parse.js.map +1 -0
- package/dist/domain/script/v3/patch/apply.d.ts +2 -0
- package/dist/domain/script/v3/patch/apply.js +31 -0
- package/dist/domain/script/v3/patch/apply.js.map +1 -0
- package/dist/domain/script/v3/patch/helpers.d.ts +17 -0
- package/dist/domain/script/v3/patch/helpers.js +209 -0
- package/dist/domain/script/v3/patch/helpers.js.map +1 -0
- package/dist/domain/script/v3/patch/inline-speaker.d.ts +2 -0
- package/dist/domain/script/v3/patch/inline-speaker.js +50 -0
- package/dist/domain/script/v3/patch/inline-speaker.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-action.d.ts +12 -0
- package/dist/domain/script/v3/patch/ops-action.js +367 -0
- package/dist/domain/script/v3/patch/ops-action.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-asset.d.ts +14 -0
- package/dist/domain/script/v3/patch/ops-asset.js +365 -0
- package/dist/domain/script/v3/patch/ops-asset.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-dialogue.d.ts +2 -0
- package/dist/domain/script/v3/patch/ops-dialogue.js +26 -0
- package/dist/domain/script/v3/patch/ops-dialogue.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-extend.d.ts +8 -0
- package/dist/domain/script/v3/patch/ops-extend.js +111 -0
- package/dist/domain/script/v3/patch/ops-extend.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-meta.d.ts +5 -0
- package/dist/domain/script/v3/patch/ops-meta.js +82 -0
- package/dist/domain/script/v3/patch/ops-meta.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-scene.d.ts +6 -0
- package/dist/domain/script/v3/patch/ops-scene.js +351 -0
- package/dist/domain/script/v3/patch/ops-scene.js.map +1 -0
- package/dist/domain/script/v3/patch/ops-state.d.ts +8 -0
- package/dist/domain/script/v3/patch/ops-state.js +165 -0
- package/dist/domain/script/v3/patch/ops-state.js.map +1 -0
- package/dist/domain/script/v3/patch/registry.d.ts +2 -0
- package/dist/domain/script/v3/patch/registry.js +53 -0
- package/dist/domain/script/v3/patch/registry.js.map +1 -0
- package/dist/domain/script/v3/patch/types.d.ts +10 -0
- package/dist/domain/script/v3/patch/types.js +2 -0
- package/dist/domain/script/v3/patch/types.js.map +1 -0
- package/dist/domain/script/v3/refs.d.ts +4 -0
- package/dist/domain/script/v3/refs.js +64 -0
- package/dist/domain/script/v3/refs.js.map +1 -0
- package/dist/domain/script/v3/scene-refs.d.ts +3 -0
- package/dist/domain/script/v3/scene-refs.js +35 -0
- package/dist/domain/script/v3/scene-refs.js.map +1 -0
- package/dist/domain/script/v3/schema.d.ts +6 -0
- package/dist/domain/script/v3/schema.js +56 -0
- package/dist/domain/script/v3/schema.js.map +1 -0
- package/dist/domain/script/v3/selector.d.ts +44 -0
- package/dist/domain/script/v3/selector.js +178 -0
- package/dist/domain/script/v3/selector.js.map +1 -0
- package/dist/domain/script/v3/types.d.ts +106 -0
- package/dist/domain/script/v3/types.js +2 -0
- package/dist/domain/script/v3/types.js.map +1 -0
- package/dist/domain/script/v3/validation.d.ts +3 -0
- package/dist/domain/script/v3/validation.js +200 -0
- package/dist/domain/script/v3/validation.js.map +1 -0
- package/dist/domain/script/v4/address.d.ts +61 -0
- package/dist/domain/script/v4/address.js +99 -0
- package/dist/domain/script/v4/address.js.map +1 -0
- package/dist/domain/script/v4/factory.d.ts +2 -0
- package/dist/domain/script/v4/factory.js +13 -0
- package/dist/domain/script/v4/factory.js.map +1 -0
- package/dist/domain/script/v4/from-v3.d.ts +29 -0
- package/dist/domain/script/v4/from-v3.js +307 -0
- package/dist/domain/script/v4/from-v3.js.map +1 -0
- package/dist/domain/script/v4/ids.d.ts +27 -0
- package/dist/domain/script/v4/ids.js +62 -0
- package/dist/domain/script/v4/ids.js.map +1 -0
- package/dist/domain/script/v4/ordinal.d.ts +3 -0
- package/dist/domain/script/v4/ordinal.js +66 -0
- package/dist/domain/script/v4/ordinal.js.map +1 -0
- package/dist/domain/script/v4/parse.d.ts +3 -0
- package/dist/domain/script/v4/parse.js +17 -0
- package/dist/domain/script/v4/parse.js.map +1 -0
- package/dist/domain/script/v4/patch/apply.d.ts +3 -0
- package/dist/domain/script/v4/patch/apply.js +24 -0
- package/dist/domain/script/v4/patch/apply.js.map +1 -0
- package/dist/domain/script/v4/patch/helpers.d.ts +32 -0
- package/dist/domain/script/v4/patch/helpers.js +138 -0
- package/dist/domain/script/v4/patch/helpers.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-action.d.ts +13 -0
- package/dist/domain/script/v4/patch/ops-action.js +376 -0
- package/dist/domain/script/v4/patch/ops-action.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-asset.d.ts +17 -0
- package/dist/domain/script/v4/patch/ops-asset.js +617 -0
- package/dist/domain/script/v4/patch/ops-asset.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-extend.d.ts +11 -0
- package/dist/domain/script/v4/patch/ops-extend.js +86 -0
- package/dist/domain/script/v4/patch/ops-extend.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-meta.d.ts +5 -0
- package/dist/domain/script/v4/patch/ops-meta.js +49 -0
- package/dist/domain/script/v4/patch/ops-meta.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-scene-ref.d.ts +3 -0
- package/dist/domain/script/v4/patch/ops-scene-ref.js +98 -0
- package/dist/domain/script/v4/patch/ops-scene-ref.js.map +1 -0
- package/dist/domain/script/v4/patch/ops-structure.d.ts +6 -0
- package/dist/domain/script/v4/patch/ops-structure.js +236 -0
- package/dist/domain/script/v4/patch/ops-structure.js.map +1 -0
- package/dist/domain/script/v4/patch/registry.d.ts +2 -0
- package/dist/domain/script/v4/patch/registry.js +50 -0
- package/dist/domain/script/v4/patch/registry.js.map +1 -0
- package/dist/domain/script/v4/patch/schema.d.ts +7 -0
- package/dist/domain/script/v4/patch/schema.js +75 -0
- package/dist/domain/script/v4/patch/schema.js.map +1 -0
- package/dist/domain/script/v4/patch/types.d.ts +10 -0
- package/dist/domain/script/v4/patch/types.js +2 -0
- package/dist/domain/script/v4/patch/types.js.map +1 -0
- package/dist/domain/script/v4/selector.d.ts +11 -0
- package/dist/domain/script/v4/selector.js +124 -0
- package/dist/domain/script/v4/selector.js.map +1 -0
- package/dist/domain/script/v4/types.d.ts +120 -0
- package/dist/domain/script/v4/types.js +2 -0
- package/dist/domain/script/v4/types.js.map +1 -0
- package/dist/domain/script/v4/validation.d.ts +2 -0
- package/dist/domain/script/v4/validation.js +378 -0
- package/dist/domain/script/v4/validation.js.map +1 -0
- package/dist/infra/ingest/video-discovery.js +7 -4
- package/dist/infra/ingest/video-discovery.js.map +1 -1
- package/dist/infra/script-output-store.js +6 -11
- package/dist/infra/script-output-store.js.map +1 -1
- package/dist/infra/script-working-copy.js +15 -16
- package/dist/infra/script-working-copy.js.map +1 -1
- package/dist/llm/config.js +5 -4
- package/dist/llm/config.js.map +1 -1
- package/dist/llm/registry.d.ts +1 -0
- package/dist/llm/registry.js +3 -0
- package/dist/llm/registry.js.map +1 -1
- package/dist/llm/router.js +6 -8
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/tasks/ingest/asset-audit.d.ts +6 -0
- package/dist/llm/tasks/ingest/asset-audit.js +164 -0
- package/dist/llm/tasks/ingest/asset-audit.js.map +1 -0
- package/dist/llm/tasks/ingest/asset-curate.d.ts +1 -1
- package/dist/llm/tasks/ingest/text-normalize.js +3 -3
- package/dist/llm/tasks/ingest/text-normalize.js.map +1 -1
- package/dist/llm/tasks/ingest/video-serial.js +3 -3
- package/dist/llm/tasks/ingest/video-serial.js.map +1 -1
- package/dist/llm/types.d.ts +1 -1
- package/dist/llm/types.js.map +1 -1
- package/dist/output.d.ts +2 -3
- package/dist/output.js +72 -109
- package/dist/output.js.map +1 -1
- package/dist/usecases/changelog.d.ts +2 -2
- package/dist/usecases/changelog.js +28 -36
- package/dist/usecases/changelog.js.map +1 -1
- package/dist/usecases/doctor.d.ts +2 -2
- package/dist/usecases/doctor.js +3 -5
- package/dist/usecases/doctor.js.map +1 -1
- package/dist/usecases/ingest/command.d.ts +2 -2
- package/dist/usecases/ingest/command.js +23 -6
- package/dist/usecases/ingest/command.js.map +1 -1
- package/dist/usecases/ingest/final-asset-audit.d.ts +39 -0
- package/dist/usecases/ingest/final-asset-audit.js +88 -0
- package/dist/usecases/ingest/final-asset-audit.js.map +1 -0
- package/dist/usecases/ingest/pass5-extract.js +5 -4
- package/dist/usecases/ingest/pass5-extract.js.map +1 -1
- package/dist/usecases/ingest/pass5-govern.js +13 -11
- package/dist/usecases/ingest/pass5-govern.js.map +1 -1
- package/dist/usecases/ingest/pipeline.js +77 -23
- package/dist/usecases/ingest/pipeline.js.map +1 -1
- package/dist/usecases/ingest/serial-govern.d.ts +2 -1
- package/dist/usecases/ingest/serial-govern.js +16 -8
- package/dist/usecases/ingest/serial-govern.js.map +1 -1
- package/dist/usecases/ingest/status.d.ts +2 -2
- package/dist/usecases/ingest/status.js +1 -1
- package/dist/usecases/ingest/status.js.map +1 -1
- package/dist/usecases/ingest/video-pipeline.js +121 -29
- package/dist/usecases/ingest/video-pipeline.js.map +1 -1
- package/dist/usecases/ingest/view-command.d.ts +2 -2
- package/dist/usecases/ingest/view-command.js +7 -6
- package/dist/usecases/ingest/view-command.js.map +1 -1
- package/dist/usecases/ingest/view-server.d.ts +2 -2
- package/dist/usecases/ingest/view-server.js +9 -7
- package/dist/usecases/ingest/view-server.js.map +1 -1
- package/dist/usecases/script/actions.d.ts +2 -2
- package/dist/usecases/script/actions.js +106 -11
- package/dist/usecases/script/actions.js.map +1 -1
- package/dist/usecases/script/actor.d.ts +2 -2
- package/dist/usecases/script/actor.js +3 -4
- package/dist/usecases/script/actor.js.map +1 -1
- package/dist/usecases/script/actors.d.ts +2 -2
- package/dist/usecases/script/actors.js +18 -9
- package/dist/usecases/script/actors.js.map +1 -1
- package/dist/usecases/script/add-actor.d.ts +2 -2
- package/dist/usecases/script/add-episode.d.ts +2 -2
- package/dist/usecases/script/add-location.d.ts +2 -2
- package/dist/usecases/script/add-prop.d.ts +2 -2
- package/dist/usecases/script/alias.d.ts +2 -2
- package/dist/usecases/script/alias.js +13 -14
- package/dist/usecases/script/alias.js.map +1 -1
- package/dist/usecases/script/assets.d.ts +2 -2
- package/dist/usecases/script/assets.js +29 -5
- package/dist/usecases/script/assets.js.map +1 -1
- package/dist/usecases/script/audit-assets.d.ts +4 -0
- package/dist/usecases/script/audit-assets.js +37 -0
- package/dist/usecases/script/audit-assets.js.map +1 -0
- package/dist/usecases/script/command-constraints.d.ts +36 -0
- package/dist/usecases/script/command-constraints.js +114 -0
- package/dist/usecases/script/command-constraints.js.map +1 -0
- package/dist/usecases/script/context.d.ts +2 -2
- package/dist/usecases/script/context.js +34 -8
- package/dist/usecases/script/context.js.map +1 -1
- package/dist/usecases/script/create.d.ts +2 -2
- package/dist/usecases/script/create.js +5 -6
- package/dist/usecases/script/create.js.map +1 -1
- package/dist/usecases/script/delete.d.ts +2 -2
- package/dist/usecases/script/delete.js +15 -15
- package/dist/usecases/script/delete.js.map +1 -1
- package/dist/usecases/script/describe.d.ts +2 -2
- package/dist/usecases/script/describe.js +13 -12
- package/dist/usecases/script/describe.js.map +1 -1
- package/dist/usecases/script/dialogue.d.ts +2 -2
- package/dist/usecases/script/dialogue.js +18 -9
- package/dist/usecases/script/dialogue.js.map +1 -1
- package/dist/usecases/script/dispatch.d.ts +3 -0
- package/dist/usecases/script/dispatch.js +10 -0
- package/dist/usecases/script/dispatch.js.map +1 -0
- package/dist/usecases/script/do.d.ts +2 -2
- package/dist/usecases/script/do.js +105 -45
- package/dist/usecases/script/do.js.map +1 -1
- package/dist/usecases/script/emotion.d.ts +2 -2
- package/dist/usecases/script/emotion.js +14 -3
- package/dist/usecases/script/emotion.js.map +1 -1
- package/dist/usecases/script/episodes.d.ts +2 -2
- package/dist/usecases/script/episodes.js +19 -8
- package/dist/usecases/script/episodes.js.map +1 -1
- package/dist/usecases/script/export-authoring.d.ts +2 -2
- package/dist/usecases/script/export-authoring.js +47 -26
- package/dist/usecases/script/export-authoring.js.map +1 -1
- package/dist/usecases/script/extend.d.ts +2 -2
- package/dist/usecases/script/extend.js +43 -31
- package/dist/usecases/script/extend.js.map +1 -1
- package/dist/usecases/script/ids.d.ts +2 -2
- package/dist/usecases/script/ids.js +68 -21
- package/dist/usecases/script/ids.js.map +1 -1
- package/dist/usecases/script/importance.d.ts +2 -2
- package/dist/usecases/script/importance.js +3 -4
- package/dist/usecases/script/importance.js.map +1 -1
- package/dist/usecases/script/insert.d.ts +2 -2
- package/dist/usecases/script/insert.js +80 -46
- package/dist/usecases/script/insert.js.map +1 -1
- package/dist/usecases/script/issues.d.ts +2 -2
- package/dist/usecases/script/issues.js +10 -2
- package/dist/usecases/script/issues.js.map +1 -1
- package/dist/usecases/script/lib.d.ts +62 -7
- package/dist/usecases/script/lib.js +782 -184
- package/dist/usecases/script/lib.js.map +1 -1
- package/dist/usecases/script/locations.d.ts +2 -2
- package/dist/usecases/script/locations.js +18 -9
- package/dist/usecases/script/locations.js.map +1 -1
- package/dist/usecases/script/merge.d.ts +2 -2
- package/dist/usecases/script/merge.js +17 -17
- package/dist/usecases/script/merge.js.map +1 -1
- package/dist/usecases/script/move.d.ts +2 -2
- package/dist/usecases/script/move.js +30 -13
- package/dist/usecases/script/move.js.map +1 -1
- package/dist/usecases/script/parenthetical.d.ts +3 -0
- package/dist/usecases/script/parenthetical.js +52 -0
- package/dist/usecases/script/parenthetical.js.map +1 -0
- package/dist/usecases/script/props.d.ts +2 -2
- package/dist/usecases/script/props.js +18 -9
- package/dist/usecases/script/props.js.map +1 -1
- package/dist/usecases/script/refs.d.ts +2 -2
- package/dist/usecases/script/refs.js +61 -11
- package/dist/usecases/script/refs.js.map +1 -1
- package/dist/usecases/script/rename.d.ts +2 -2
- package/dist/usecases/script/rename.js +13 -12
- package/dist/usecases/script/rename.js.map +1 -1
- package/dist/usecases/script/replace.d.ts +2 -2
- package/dist/usecases/script/replace.js +6 -8
- package/dist/usecases/script/replace.js.map +1 -1
- package/dist/usecases/script/role.d.ts +2 -2
- package/dist/usecases/script/role.js +3 -4
- package/dist/usecases/script/role.js.map +1 -1
- package/dist/usecases/script/scenes.d.ts +2 -2
- package/dist/usecases/script/scenes.js +35 -7
- package/dist/usecases/script/scenes.js.map +1 -1
- package/dist/usecases/script/script-patch.d.ts +2 -2
- package/dist/usecases/script/script-patch.js +219 -72
- package/dist/usecases/script/script-patch.js.map +1 -1
- package/dist/usecases/script/script-validate.d.ts +2 -2
- package/dist/usecases/script/script-validate.js +11 -7
- package/dist/usecases/script/script-validate.js.map +1 -1
- package/dist/usecases/script/session.d.ts +3 -0
- package/dist/usecases/script/session.js +74 -65
- package/dist/usecases/script/session.js.map +1 -1
- package/dist/usecases/script/split.d.ts +2 -2
- package/dist/usecases/script/split.js +18 -14
- package/dist/usecases/script/split.js.map +1 -1
- package/dist/usecases/script/state-add.d.ts +2 -2
- package/dist/usecases/script/state-add.js +3 -4
- package/dist/usecases/script/state-add.js.map +1 -1
- package/dist/usecases/script/state-delete.d.ts +2 -2
- package/dist/usecases/script/state-delete.js +3 -4
- package/dist/usecases/script/state-delete.js.map +1 -1
- package/dist/usecases/script/state-rename.d.ts +2 -2
- package/dist/usecases/script/state-rename.js +3 -4
- package/dist/usecases/script/state-rename.js.map +1 -1
- package/dist/usecases/script/states.d.ts +2 -2
- package/dist/usecases/script/states.js +91 -20
- package/dist/usecases/script/states.js.map +1 -1
- package/dist/usecases/script/summary.d.ts +2 -2
- package/dist/usecases/script/summary.js +21 -2
- package/dist/usecases/script/summary.js.map +1 -1
- package/dist/usecases/script/synopsis-generate.d.ts +2 -2
- package/dist/usecases/script/synopsis-generate.js +38 -39
- package/dist/usecases/script/synopsis-generate.js.map +1 -1
- package/dist/usecases/script/synopsis.d.ts +2 -2
- package/dist/usecases/script/synopsis.js +20 -16
- package/dist/usecases/script/synopsis.js.map +1 -1
- package/dist/usecases/script/transition.d.ts +2 -2
- package/dist/usecases/script/transition.js +3 -4
- package/dist/usecases/script/transition.js.map +1 -1
- package/dist/usecases/script/type.d.ts +2 -2
- package/dist/usecases/script/type.js +3 -4
- package/dist/usecases/script/type.js.map +1 -1
- package/dist/usecases/script/v3/mutations.d.ts +2 -0
- package/dist/usecases/script/v3/mutations.js +5 -0
- package/dist/usecases/script/v3/mutations.js.map +1 -0
- package/dist/usecases/script/v4/mutations.d.ts +2 -0
- package/dist/usecases/script/v4/mutations.js +6 -0
- package/dist/usecases/script/v4/mutations.js.map +1 -0
- package/dist/usecases/script/v4/queries.d.ts +15 -0
- package/dist/usecases/script/v4/queries.js +191 -0
- package/dist/usecases/script/v4/queries.js.map +1 -0
- package/dist/usecases/script/working-copy.d.ts +4 -4
- package/dist/usecases/script/working-copy.js +98 -94
- package/dist/usecases/script/working-copy.js.map +1 -1
- package/dist/usecases/script/worldview.d.ts +2 -2
- package/dist/usecases/script/worldview.js +3 -4
- package/dist/usecases/script/worldview.js.map +1 -1
- package/dist/usecases/script/write-authoring.d.ts +2 -2
- package/dist/usecases/script/write-authoring.js +125 -50
- package/dist/usecases/script/write-authoring.js.map +1 -1
- package/dist/usecases/update.d.ts +2 -2
- package/dist/usecases/update.js +32 -47
- package/dist/usecases/update.js.map +1 -1
- package/package.json +3 -2
- package/skills/scriptctl/SKILL.md +128 -282
- package/skills/scriptctl/references/atomic-edit-workflow.md +23 -38
- package/skills/scriptctl/references/bulk-authoring.md +35 -154
- package/skills/scriptctl/references/ingest-workflow.md +12 -11
- package/skills/scriptctl/references/production-writing-standard.md +38 -67
- package/skills/scriptctl/references/state-reference-repair.md +19 -42
- package/dist/help-text.d.ts +0 -2
- package/dist/help-text.js +0 -1112
- package/dist/help-text.js.map +0 -1
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { CliError, EXIT_NEEDS_AGENT, EXIT_OK, EXIT_USAGE, } from "../../common.js";
|
|
2
|
-
import {
|
|
1
|
+
import { CliError, EXIT_INPUT, EXIT_NEEDS_AGENT, EXIT_OK, EXIT_USAGE, sha256Text, } from "../../common.js";
|
|
2
|
+
import { applyPatchOperationsV4 } from "../../domain/script/v4/patch/apply.js";
|
|
3
|
+
import { isV4Id } from "../../domain/script/v4/ids.js";
|
|
4
|
+
import { requireScriptV4 } from "../../domain/script/v4/parse.js";
|
|
5
|
+
import { validateScriptV4 } from "../../domain/script/v4/validation.js";
|
|
3
6
|
import { sceneRefs } from "../../domain/script/scene-refs.js";
|
|
4
7
|
import { isDangerousRegex, MAX_GREP_PATTERN_LEN, parseInSelector, selectorSingleActionPoint, WHOLE_SCRIPT, } from "../../domain/script/selector.js";
|
|
5
8
|
import { parseAnyAddress, } from "../../domain/script/address.js";
|
|
9
|
+
import { parseV4Address } from "../../domain/script/v4/address.js";
|
|
6
10
|
import { summarizeIssues } from "../../domain/script/text-util.js";
|
|
7
11
|
import { sortDeep } from "../../domain/script/text-util.js";
|
|
8
12
|
import { asList, idNum, isDict, strOf, } from "../../domain/script/shared.js";
|
|
9
13
|
import { loadScriptForEdit, validateSession, saveScriptSession, syncValidationResult, scriptSourceText, } from "./session.js";
|
|
14
|
+
import { resolveActionTargetsV4 } from "./v4/queries.js";
|
|
15
|
+
import { applyVersionedMutations } from "./dispatch.js";
|
|
16
|
+
import { assertInsertCommandFlags, assertMoveCommandFlags, assertSceneRefCommandFlags, } from "./command-constraints.js";
|
|
10
17
|
export function tokenizeLine(line) {
|
|
11
18
|
const out = [];
|
|
12
19
|
let cur = "";
|
|
@@ -75,6 +82,7 @@ export const DO_BOOL_FLAGS = {
|
|
|
75
82
|
replace: new Set(["all", "regex"]),
|
|
76
83
|
transition: new Set(["clear"]),
|
|
77
84
|
emotion: new Set(["clear"]),
|
|
85
|
+
parenthetical: new Set(["remove"]),
|
|
78
86
|
"scene-ref": new Set(["clear", "remove", "append"]),
|
|
79
87
|
delete: new Set(["force"]),
|
|
80
88
|
extend: new Set(["clear", "json-value"]),
|
|
@@ -82,6 +90,8 @@ export const DO_BOOL_FLAGS = {
|
|
|
82
90
|
export const DO_REPEAT_FLAGS = {
|
|
83
91
|
alias: new Set(["add", "remove"]),
|
|
84
92
|
"add-actor": new Set(["alias"]),
|
|
93
|
+
"scene-ref": new Set(["state"]),
|
|
94
|
+
insert: new Set(["state"]),
|
|
85
95
|
};
|
|
86
96
|
function pushFlag(flags, repeats, name, val) {
|
|
87
97
|
if (repeats.has(name)) {
|
|
@@ -145,12 +155,11 @@ export function parseDoLine(tokens) {
|
|
|
145
155
|
return { verb, args, flags };
|
|
146
156
|
}
|
|
147
157
|
export function doErr(detail) {
|
|
148
|
-
return new CliError(
|
|
158
|
+
return new CliError(detail, {
|
|
149
159
|
exitCode: EXIT_USAGE,
|
|
150
|
-
required: ["a valid <verb> ...args... line"],
|
|
151
|
-
received: [detail],
|
|
152
|
-
nextSteps: ["Each line is a scriptctl verb minus the leading `scriptctl`; see `scriptctl do --help`."],
|
|
153
160
|
errorCode: "DO_LINE_INVALID",
|
|
161
|
+
hint: "Each line is a scriptctl verb minus the leading `scriptctl`; see `scriptctl do --help`.",
|
|
162
|
+
data: { expected: ["a valid <verb> ...args... line"], actual: [detail] },
|
|
154
163
|
});
|
|
155
164
|
}
|
|
156
165
|
// ---- shared op-template builders ------------------------------------------
|
|
@@ -162,36 +171,46 @@ export function stateChangeTemplate(target, f) {
|
|
|
162
171
|
const toSet = f.to !== undefined;
|
|
163
172
|
const clearSet = Boolean(f.clear);
|
|
164
173
|
if (toSet && clearSet) {
|
|
165
|
-
throw new CliError("
|
|
166
|
-
exitCode: EXIT_USAGE,
|
|
167
|
-
|
|
174
|
+
throw new CliError("state-change: --to and --clear are mutually exclusive.", {
|
|
175
|
+
exitCode: EXIT_USAGE,
|
|
176
|
+
errorCode: "STATE_CHANGE_FLAG_CONFLICT",
|
|
177
|
+
hint: "Pick one and retry.",
|
|
178
|
+
data: { expected: ["exactly one of --to <state> / --clear"], actual: ["--to", "--clear"] },
|
|
168
179
|
});
|
|
169
180
|
}
|
|
170
181
|
if (!toSet && !clearSet) {
|
|
171
|
-
throw new CliError("
|
|
172
|
-
exitCode: EXIT_USAGE,
|
|
173
|
-
|
|
182
|
+
throw new CliError("state-change: must specify --to <state> or --clear.", {
|
|
183
|
+
exitCode: EXIT_USAGE,
|
|
184
|
+
errorCode: "STATE_CHANGE_FLAG_MISSING",
|
|
185
|
+
hint: "Pick exactly one flag.",
|
|
186
|
+
data: { expected: ["one of --to <state> / --clear"], actual: ["<missing>"] },
|
|
174
187
|
});
|
|
175
188
|
}
|
|
176
189
|
// v3 has no per-action state_changes; state lives on scene.ref.* only.
|
|
177
|
-
throw new CliError("
|
|
178
|
-
exitCode: EXIT_USAGE,
|
|
179
|
-
|
|
190
|
+
throw new CliError("state-change was removed in v3; set a scene ref's state instead.", {
|
|
191
|
+
exitCode: EXIT_USAGE,
|
|
192
|
+
errorCode: "STATE_CHANGE_REMOVED",
|
|
193
|
+
hint: "Use `scene-ref <ep/scn> <kind:id> --state <state_id>` (op scene.ref.set).",
|
|
194
|
+
data: { expected: ["scene-ref state edit"], actual: ["state-change"] },
|
|
180
195
|
});
|
|
181
196
|
}
|
|
182
197
|
export function transitionTemplate(target, f) {
|
|
183
198
|
const setBoth = f.process !== undefined || f.contrast !== undefined;
|
|
184
199
|
const clearSet = Boolean(f.clear);
|
|
185
200
|
if (setBoth && clearSet) {
|
|
186
|
-
throw new CliError("
|
|
187
|
-
exitCode: EXIT_USAGE,
|
|
188
|
-
|
|
201
|
+
throw new CliError("transition: --clear is mutually exclusive with --process/--contrast.", {
|
|
202
|
+
exitCode: EXIT_USAGE,
|
|
203
|
+
errorCode: "TRANSITION_FLAG_CONFLICT",
|
|
204
|
+
hint: "Pick one mode and retry.",
|
|
205
|
+
data: { expected: ["either --clear, or --process + --contrast"], actual: ["--clear", "--process/--contrast"] },
|
|
189
206
|
});
|
|
190
207
|
}
|
|
191
208
|
if (!setBoth && !clearSet) {
|
|
192
|
-
throw new CliError("
|
|
193
|
-
exitCode: EXIT_USAGE,
|
|
194
|
-
|
|
209
|
+
throw new CliError("transition: must specify --process/--contrast or --clear.", {
|
|
210
|
+
exitCode: EXIT_USAGE,
|
|
211
|
+
errorCode: "TRANSITION_FLAG_MISSING",
|
|
212
|
+
hint: "Pick exactly one mode.",
|
|
213
|
+
data: { expected: ["--process + --contrast, or --clear"], actual: ["<missing>"] },
|
|
195
214
|
});
|
|
196
215
|
}
|
|
197
216
|
return clearSet
|
|
@@ -202,15 +221,19 @@ export function emotionTemplate(f) {
|
|
|
202
221
|
const emotionSet = f.emotion !== undefined;
|
|
203
222
|
const clearSet = Boolean(f.clear);
|
|
204
223
|
if (emotionSet && clearSet) {
|
|
205
|
-
throw new CliError("
|
|
206
|
-
exitCode: EXIT_USAGE,
|
|
207
|
-
|
|
224
|
+
throw new CliError("emotion: value and --clear are mutually exclusive.", {
|
|
225
|
+
exitCode: EXIT_USAGE,
|
|
226
|
+
errorCode: "EMOTION_FLAG_CONFLICT",
|
|
227
|
+
hint: "Pick one mode and retry.",
|
|
228
|
+
data: { expected: ["either <emotion>, or --clear"], actual: ["<emotion>", "--clear"] },
|
|
208
229
|
});
|
|
209
230
|
}
|
|
210
231
|
if (!emotionSet && !clearSet) {
|
|
211
|
-
throw new CliError("
|
|
212
|
-
exitCode: EXIT_USAGE,
|
|
213
|
-
|
|
232
|
+
throw new CliError("emotion: must specify <emotion> or --clear.", {
|
|
233
|
+
exitCode: EXIT_USAGE,
|
|
234
|
+
errorCode: "EMOTION_VALUE_MISSING",
|
|
235
|
+
hint: "Pass an emotion label, or use --clear.",
|
|
236
|
+
data: { expected: ["<emotion>, or --clear"], actual: ["<missing>"] },
|
|
214
237
|
});
|
|
215
238
|
}
|
|
216
239
|
return clearSet
|
|
@@ -223,9 +246,11 @@ export function contextTemplate(target, f) {
|
|
|
223
246
|
const removeSet = Boolean(f.remove);
|
|
224
247
|
const n = (stateSet ? 1 : 0) + (clearSet ? 1 : 0) + (removeSet ? 1 : 0);
|
|
225
248
|
if (n === 0) {
|
|
226
|
-
throw new CliError("
|
|
227
|
-
exitCode: EXIT_USAGE,
|
|
228
|
-
|
|
249
|
+
throw new CliError("context: must specify --state X, --clear, or --remove.", {
|
|
250
|
+
exitCode: EXIT_USAGE,
|
|
251
|
+
errorCode: "CONTEXT_FLAG_MISSING",
|
|
252
|
+
hint: "Pick exactly one flag.",
|
|
253
|
+
data: { expected: ["one of --state <state_id> / --clear / --remove"], actual: ["<missing>"] },
|
|
229
254
|
});
|
|
230
255
|
}
|
|
231
256
|
if (n > 1) {
|
|
@@ -236,30 +261,30 @@ export function contextTemplate(target, f) {
|
|
|
236
261
|
passed.push("--clear");
|
|
237
262
|
if (removeSet)
|
|
238
263
|
passed.push("--remove");
|
|
239
|
-
throw new CliError("
|
|
240
|
-
exitCode: EXIT_USAGE,
|
|
241
|
-
|
|
264
|
+
throw new CliError("context: --state / --clear / --remove are mutually exclusive.", {
|
|
265
|
+
exitCode: EXIT_USAGE,
|
|
266
|
+
errorCode: "CONTEXT_FLAG_CONFLICT",
|
|
267
|
+
hint: "Pick one and retry.",
|
|
268
|
+
data: { expected: ["exactly one of --state / --clear / --remove"], actual: passed },
|
|
242
269
|
});
|
|
243
270
|
}
|
|
244
271
|
const append = Boolean(f.append);
|
|
245
272
|
const occurrenceRaw = strOf(f.occurrence).trim();
|
|
246
273
|
const occurrence = occurrenceRaw ? Number(occurrenceRaw) : undefined;
|
|
247
274
|
if (occurrence !== undefined && (!Number.isInteger(occurrence) || occurrence < 1)) {
|
|
248
|
-
throw new CliError("
|
|
275
|
+
throw new CliError("scene-ref: --occurrence must be a positive integer.", {
|
|
249
276
|
exitCode: EXIT_USAGE,
|
|
250
|
-
required: ["--occurrence <positive integer> (1-based)"],
|
|
251
|
-
received: [occurrenceRaw],
|
|
252
|
-
nextSteps: ["Use 1 for the first matching scene ref, 2 for the second, and so on."],
|
|
253
277
|
errorCode: "SCENE_REF_OCCURRENCE_INVALID",
|
|
278
|
+
hint: "Use 1 for the first matching scene ref, 2 for the second, and so on.",
|
|
279
|
+
data: { expected: ["--occurrence <positive integer> (1-based)"], actual: [occurrenceRaw] },
|
|
254
280
|
});
|
|
255
281
|
}
|
|
256
282
|
if (append && (!stateSet || occurrence !== undefined)) {
|
|
257
|
-
throw new CliError("
|
|
283
|
+
throw new CliError("scene-ref: --append requires --state and cannot be combined with --occurrence.", {
|
|
258
284
|
exitCode: EXIT_USAGE,
|
|
259
|
-
required: ["--state <state_id> --append"],
|
|
260
|
-
received: [append ? "--append" : "", occurrence !== undefined ? "--occurrence" : "", clearSet ? "--clear" : "", removeSet ? "--remove" : ""].filter(Boolean),
|
|
261
|
-
nextSteps: ["Append a new actor ref with --state <state_id> --append, or target an existing ref with --occurrence <n>."],
|
|
262
285
|
errorCode: "SCENE_REF_APPEND_FLAG_CONFLICT",
|
|
286
|
+
hint: "Append a new actor ref with --state <state_id> --append, or target an existing ref with --occurrence <n>.",
|
|
287
|
+
data: { expected: ["--state <state_id> --append"], actual: [append ? "--append" : "", occurrence !== undefined ? "--occurrence" : "", clearSet ? "--clear" : "", removeSet ? "--remove" : ""].filter(Boolean) },
|
|
263
288
|
});
|
|
264
289
|
}
|
|
265
290
|
if (removeSet)
|
|
@@ -275,39 +300,67 @@ export function dialogueTemplate(f) {
|
|
|
275
300
|
const speakerKind = strOf(f.kind).trim();
|
|
276
301
|
const label = strOf(f.label).trim();
|
|
277
302
|
if (actorId && speakerKind) {
|
|
278
|
-
throw new CliError("
|
|
303
|
+
throw new CliError("dialogue: --actor and --kind are mutually exclusive.", {
|
|
279
304
|
exitCode: EXIT_USAGE,
|
|
280
|
-
required: ["exactly one of --actor <actor_id> or --kind <non-actor kind>"],
|
|
281
|
-
received: ["--actor", "--kind"],
|
|
282
|
-
nextSteps: ["Keep --actor for a character, or --kind for a non-actor source."],
|
|
283
305
|
errorCode: "SPEAKER_AMBIGUOUS",
|
|
306
|
+
hint: "Keep --actor for a character, or --kind for a non-actor source.",
|
|
307
|
+
data: { expected: ["exactly one of --actor <actor_id> or --kind <non-actor kind>"], actual: ["--actor", "--kind"] },
|
|
284
308
|
});
|
|
285
309
|
}
|
|
286
310
|
if (actorId && label) {
|
|
287
|
-
throw new CliError("
|
|
311
|
+
throw new CliError("dialogue: --label is only valid with --kind.", {
|
|
288
312
|
exitCode: EXIT_USAGE,
|
|
289
|
-
required: ["omit --label with --actor, or pair --label with --kind"],
|
|
290
|
-
received: ["--actor", "--label"],
|
|
291
|
-
nextSteps: ["Remove --label for an actor line, or use --kind for a labeled non-actor source."],
|
|
292
313
|
errorCode: "SPEAKER_LABEL_WITHOUT_KIND",
|
|
314
|
+
hint: "Remove --label for an actor line, or use --kind for a labeled non-actor source.",
|
|
315
|
+
data: { expected: ["omit --label with --actor, or pair --label with --kind"], actual: ["--actor", "--label"] },
|
|
293
316
|
});
|
|
294
317
|
}
|
|
295
318
|
if (actorId)
|
|
296
319
|
return { op: "dialogue.speaker.set", actor_id: actorId };
|
|
297
320
|
if (speakerKind)
|
|
298
321
|
return { op: "dialogue.speaker.set", kind: speakerKind, label: label || undefined };
|
|
299
|
-
throw new CliError("
|
|
300
|
-
exitCode: EXIT_USAGE,
|
|
301
|
-
|
|
322
|
+
throw new CliError("dialogue: pass --actor <id> or --kind <system|broadcast|offscreen|group>.", {
|
|
323
|
+
exitCode: EXIT_USAGE,
|
|
324
|
+
errorCode: "DIALOGUE_SPEAKER_MISSING",
|
|
325
|
+
hint: "Set --actor for an actor line, or --kind for a non-actor line.",
|
|
326
|
+
data: { expected: ["--actor <actor_id> or --kind <non-actor kind>"], actual: ["<missing>"] },
|
|
302
327
|
});
|
|
303
328
|
}
|
|
304
329
|
// Translate one parsed verb line into a patch op dict (the SAME ops `patch`
|
|
305
330
|
// consumes). `do` = the human/agent-friendly notation for those ops: no JSON,
|
|
306
331
|
// no field-name guessing — the verbs you already type. Multipolar verbs
|
|
307
332
|
// (delete/move/insert/merge/describe) pick their op by the address kind.
|
|
308
|
-
|
|
333
|
+
function repeatedDoFlag(raw) {
|
|
334
|
+
if (raw === undefined)
|
|
335
|
+
return [];
|
|
336
|
+
return Array.isArray(raw) ? raw.map(strOf) : [strOf(raw)];
|
|
337
|
+
}
|
|
338
|
+
function doInsertFlags(flags) {
|
|
339
|
+
return {
|
|
340
|
+
type: flags["type"],
|
|
341
|
+
content: flags["content"],
|
|
342
|
+
emotion: flags["emotion"],
|
|
343
|
+
parenthetical: flags["parenthetical"],
|
|
344
|
+
actionId: flags["action-id"],
|
|
345
|
+
at: flags["at"],
|
|
346
|
+
before: flags["before"],
|
|
347
|
+
after: flags["after"],
|
|
348
|
+
actor: flags["actor"],
|
|
349
|
+
speaker: flags["speaker"],
|
|
350
|
+
speakerState: flags["speaker-state"],
|
|
351
|
+
kind: flags["kind"],
|
|
352
|
+
label: flags["label"],
|
|
353
|
+
location: flags["location"],
|
|
354
|
+
time: flags["time"],
|
|
355
|
+
space: flags["space"],
|
|
356
|
+
sceneId: flags["scene-id"],
|
|
357
|
+
states: repeatedDoFlag(flags["state"]),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
export function lineToOp(verb, args, flags, scriptVersion = 3) {
|
|
309
361
|
const a0 = strOf(args[0]);
|
|
310
362
|
const a1 = strOf(args[1]);
|
|
363
|
+
const parseAddress = (raw) => scriptVersion === 4 ? parseV4Address(raw) : parseAnyAddress(raw);
|
|
311
364
|
switch (verb) {
|
|
312
365
|
case "replace":
|
|
313
366
|
return flags["regex"]
|
|
@@ -320,20 +373,52 @@ export function lineToOp(verb, args, flags) {
|
|
|
320
373
|
case "transition":
|
|
321
374
|
return { ...transitionTemplate(a1, { process: flags["process"], contrast: flags["contrast"], clear: Boolean(flags["clear"]) }), at: a0 };
|
|
322
375
|
case "emotion":
|
|
376
|
+
if (scriptVersion === 4)
|
|
377
|
+
throw doErr("emotion belongs to Script v3; use parenthetical for Script v4");
|
|
323
378
|
return { ...emotionTemplate({ emotion: args[1], clear: Boolean(flags["clear"]) }), at: a0 };
|
|
324
|
-
case "
|
|
379
|
+
case "parenthetical":
|
|
380
|
+
if (scriptVersion === 3)
|
|
381
|
+
throw doErr("parenthetical belongs to Script v4; use emotion for Script v3");
|
|
382
|
+
if (Boolean(flags["remove"]) === (flags["set"] !== undefined)) {
|
|
383
|
+
throw doErr("parenthetical needs exactly one of --set <text> or --remove");
|
|
384
|
+
}
|
|
385
|
+
return Boolean(flags["remove"])
|
|
386
|
+
? { op: "action.parenthetical.remove", at: a0 }
|
|
387
|
+
: { op: "action.parenthetical.set", at: a0, parenthetical: strOf(flags["set"]) };
|
|
388
|
+
case "scene-ref": {
|
|
389
|
+
const sceneRefFlags = {
|
|
390
|
+
states: repeatedDoFlag(flags["state"]),
|
|
391
|
+
clear: Boolean(flags["clear"]),
|
|
392
|
+
remove: Boolean(flags["remove"]),
|
|
393
|
+
append: Boolean(flags["append"]),
|
|
394
|
+
occurrence: flags["occurrence"],
|
|
395
|
+
};
|
|
396
|
+
assertSceneRefCommandFlags(scriptVersion, sceneRefFlags);
|
|
397
|
+
if (scriptVersion === 4) {
|
|
398
|
+
return sceneRefFlags.remove
|
|
399
|
+
? { op: "scene.ref.remove", at: a0, target: a1 }
|
|
400
|
+
: { op: "scene.ref.set", at: a0, target: a1, state_ids: sceneRefFlags.states };
|
|
401
|
+
}
|
|
325
402
|
return {
|
|
326
403
|
...contextTemplate(a1, {
|
|
327
|
-
state:
|
|
328
|
-
clear:
|
|
329
|
-
remove:
|
|
330
|
-
append:
|
|
331
|
-
occurrence:
|
|
404
|
+
state: sceneRefFlags.states[0],
|
|
405
|
+
clear: sceneRefFlags.clear,
|
|
406
|
+
remove: sceneRefFlags.remove,
|
|
407
|
+
append: sceneRefFlags.append,
|
|
408
|
+
occurrence: sceneRefFlags.occurrence,
|
|
332
409
|
}),
|
|
333
410
|
at: a0,
|
|
334
411
|
};
|
|
412
|
+
}
|
|
335
413
|
case "dialogue":
|
|
336
|
-
|
|
414
|
+
if (scriptVersion === 3 && flags["state"] !== undefined) {
|
|
415
|
+
throw doErr("dialogue --state belongs to Script v4");
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
...dialogueTemplate({ actor: flags["actor"], kind: flags["kind"], label: flags["label"] }),
|
|
419
|
+
...(scriptVersion === 4 && flags["state"] !== undefined ? { state_id: flags["state"] } : {}),
|
|
420
|
+
at: a0,
|
|
421
|
+
};
|
|
337
422
|
case "rename":
|
|
338
423
|
return { op: "asset.rename", target: a0, name: a1 };
|
|
339
424
|
case "role":
|
|
@@ -346,14 +431,15 @@ export function lineToOp(verb, args, flags) {
|
|
|
346
431
|
if (args.length > 2)
|
|
347
432
|
throw doErr("synopsis text with spaces must be quoted");
|
|
348
433
|
if (args.length === 1) {
|
|
349
|
-
if (/^ep_/.test(a0))
|
|
434
|
+
if (/^ep_/.test(a0) || /^ep[1-9A-HJ-NP-Za-km-z]{6}$/.test(a0)) {
|
|
350
435
|
throw doErr(`synopsis for ${a0} needs text`);
|
|
436
|
+
}
|
|
351
437
|
return { op: "meta.synopsis.set", synopsis: a0 };
|
|
352
438
|
}
|
|
353
|
-
const addr =
|
|
439
|
+
const addr = parseAddress(a0);
|
|
354
440
|
if (addr.kind !== "episode")
|
|
355
441
|
throw doErr(`synopsis target must be an episode id or omitted for global (${a0})`);
|
|
356
|
-
return { op: "episode.synopsis.set", episode_id: addr.episodeId, synopsis: a1 };
|
|
442
|
+
return { op: "episode.synopsis.set", episode_id: "id" in addr ? addr.id : addr.episodeId, synopsis: a1 };
|
|
357
443
|
}
|
|
358
444
|
case "alias": {
|
|
359
445
|
const add = flags["add"] ?? [];
|
|
@@ -365,7 +451,7 @@ export function lineToOp(verb, args, flags) {
|
|
|
365
451
|
throw doErr("alias needs --add <alias> or --remove <alias>");
|
|
366
452
|
}
|
|
367
453
|
case "describe": {
|
|
368
|
-
const addr =
|
|
454
|
+
const addr = parseAddress(a0);
|
|
369
455
|
if (addr.kind === "state")
|
|
370
456
|
return { op: "state.describe", target: a0, description: a1 };
|
|
371
457
|
if (addr.kind === "asset")
|
|
@@ -373,7 +459,8 @@ export function lineToOp(verb, args, flags) {
|
|
|
373
459
|
throw doErr(`describe cannot operate on a ${addr.kind} address (${a0}); use an asset or asset/state target`);
|
|
374
460
|
}
|
|
375
461
|
case "extend": {
|
|
376
|
-
|
|
462
|
+
if (a0 !== "global")
|
|
463
|
+
parseAddress(a0);
|
|
377
464
|
if (flags["clear"]) {
|
|
378
465
|
if (args.length >= 3)
|
|
379
466
|
throw doErr("extend --clear takes no value (drop --clear to set, or drop the value to delete)");
|
|
@@ -399,37 +486,52 @@ export function lineToOp(verb, args, flags) {
|
|
|
399
486
|
case "state-delete":
|
|
400
487
|
return { op: "state.delete", target: a0, strategy: flags["strategy"], replacement: flags["replacement"] };
|
|
401
488
|
case "add-actor":
|
|
402
|
-
return { op: "actor.add", name: a0, role_type: flags["role"], description: flags["description"], aliases: flags["alias"] ?? undefined };
|
|
489
|
+
return { op: "actor.add", actor_id: flags["id"], name: a0, role_type: flags["role"], description: flags["description"], aliases: flags["alias"] ?? undefined };
|
|
403
490
|
case "add-location":
|
|
404
|
-
return { op: "location.add", name: a0, description: flags["description"] };
|
|
491
|
+
return { op: "location.add", location_id: flags["id"], name: a0, description: flags["description"] };
|
|
405
492
|
case "add-prop":
|
|
406
|
-
return { op: "prop.add", name: a0, description: flags["description"] };
|
|
493
|
+
return { op: "prop.add", prop_id: flags["id"], name: a0, description: flags["description"] };
|
|
407
494
|
case "add-episode":
|
|
408
495
|
return { op: "episode.add", title: flags["title"], episode_id: flags["id"] };
|
|
409
496
|
case "importance":
|
|
410
497
|
return { op: "importance.set", target: a0, importance: a1 };
|
|
411
498
|
case "split":
|
|
412
|
-
return
|
|
499
|
+
return scriptVersion === 4
|
|
500
|
+
? { op: "scene.split", at: a0, at_action: flags["at"], new_scene_id: flags["new-scene-id"] }
|
|
501
|
+
: { op: "scene.split", at: a0, at_index: Number(flags["at"]), new_scene_id: flags["new-scene-id"] };
|
|
413
502
|
case "delete": {
|
|
414
|
-
const addr =
|
|
503
|
+
const addr = parseAddress(a0);
|
|
415
504
|
if (addr.kind === "action")
|
|
416
505
|
return { op: "action.delete", at: a0 };
|
|
417
506
|
if (addr.kind === "scene")
|
|
418
507
|
return { op: "scene.delete", at: a0, force: Boolean(flags["force"]) };
|
|
508
|
+
if (addr.kind === "state")
|
|
509
|
+
return { op: "state.delete", target: a0, strategy: flags["strategy"], replacement: flags["replacement"] };
|
|
419
510
|
if (addr.kind === "asset")
|
|
420
511
|
return { op: "asset.delete", target: a0, strategy: flags["strategy"], replacement: flags["replacement"] };
|
|
421
|
-
throw doErr(`delete cannot operate on a ${addr.kind} address (${a0})
|
|
512
|
+
throw doErr(`delete cannot operate on a ${addr.kind} address (${a0})`);
|
|
422
513
|
}
|
|
423
514
|
case "move": {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
515
|
+
assertMoveCommandFlags(scriptVersion, {
|
|
516
|
+
at: flags["at"],
|
|
517
|
+
before: flags["before"],
|
|
518
|
+
after: flags["after"],
|
|
519
|
+
});
|
|
520
|
+
const addr = parseAddress(a0);
|
|
521
|
+
if (addr.kind === "scene") {
|
|
522
|
+
return scriptVersion === 4
|
|
523
|
+
? { op: "scene.move", at: a0, to: a1, before: flags["before"], after: flags["after"] }
|
|
524
|
+
: { op: "scene.move", at: a0, to: a1, at_index: flags["at"] };
|
|
525
|
+
}
|
|
526
|
+
if (addr.kind === "action") {
|
|
527
|
+
return scriptVersion === 4
|
|
528
|
+
? { op: "action.move", at: a0, to: a1, before: flags["before"], after: flags["after"] }
|
|
529
|
+
: { op: "action.move", at: a0, to: a1 };
|
|
530
|
+
}
|
|
429
531
|
throw doErr(`move cannot operate on a ${addr.kind} address (${a0}); move an action or a scene`);
|
|
430
532
|
}
|
|
431
533
|
case "merge": {
|
|
432
|
-
const addr =
|
|
534
|
+
const addr = parseAddress(a0);
|
|
433
535
|
if (addr.kind === "scene")
|
|
434
536
|
return { op: "scene.merge", from: a0, into: strOf(flags["into"]) };
|
|
435
537
|
if (addr.kind === "asset")
|
|
@@ -437,12 +539,46 @@ export function lineToOp(verb, args, flags) {
|
|
|
437
539
|
throw doErr(`merge cannot operate on a ${addr.kind} address (${a0}); merge two assets or two scenes`);
|
|
438
540
|
}
|
|
439
541
|
case "insert": {
|
|
440
|
-
const addr =
|
|
542
|
+
const addr = parseAddress(a0);
|
|
543
|
+
const insertFlags = doInsertFlags(flags);
|
|
441
544
|
if (addr.kind === "episode") {
|
|
442
|
-
|
|
545
|
+
assertInsertCommandFlags(scriptVersion, "scene", insertFlags);
|
|
546
|
+
return {
|
|
547
|
+
op: "scene.insert",
|
|
548
|
+
at: a0,
|
|
549
|
+
location: flags["location"],
|
|
550
|
+
time: flags["time"],
|
|
551
|
+
space: flags["space"],
|
|
552
|
+
...(scriptVersion === 4
|
|
553
|
+
? { location_state_ids: insertFlags.states, before: flags["before"], after: flags["after"] }
|
|
554
|
+
: { at_index: flags["at"], before: flags["before"], after: flags["after"] }),
|
|
555
|
+
scene_id: flags["scene-id"],
|
|
556
|
+
};
|
|
443
557
|
}
|
|
444
558
|
if (addr.kind === "scene") {
|
|
445
|
-
|
|
559
|
+
assertInsertCommandFlags(scriptVersion, "action", insertFlags);
|
|
560
|
+
return {
|
|
561
|
+
op: "action.insert",
|
|
562
|
+
at: a0,
|
|
563
|
+
type: flags["type"],
|
|
564
|
+
content: flags["content"],
|
|
565
|
+
before: flags["before"],
|
|
566
|
+
after: flags["after"],
|
|
567
|
+
actor_id: flags["actor"],
|
|
568
|
+
kind: flags["kind"],
|
|
569
|
+
label: flags["label"],
|
|
570
|
+
...(scriptVersion === 4
|
|
571
|
+
? {
|
|
572
|
+
action_id: flags["action-id"],
|
|
573
|
+
state_id: flags["speaker-state"],
|
|
574
|
+
parenthetical: flags["parenthetical"],
|
|
575
|
+
}
|
|
576
|
+
: {
|
|
577
|
+
at_index: flags["at"],
|
|
578
|
+
speaker_id: flags["speaker"],
|
|
579
|
+
emotion: flags["emotion"],
|
|
580
|
+
}),
|
|
581
|
+
};
|
|
446
582
|
}
|
|
447
583
|
throw doErr(`insert needs ep_NNN (scene insert) or ep_NNN/scn_NNN (action insert), got a ${addr.kind} address (${a0})`);
|
|
448
584
|
}
|
|
@@ -519,65 +655,402 @@ export const SKIPPABLE_EDIT_CODES = new Set([
|
|
|
519
655
|
"FROM_TEXT_NOT_FOUND",
|
|
520
656
|
"OVERLAP_DIALOGUE_UNSUPPORTED",
|
|
521
657
|
]);
|
|
658
|
+
export function applyPatchOperationsForSession(session, source, operations) {
|
|
659
|
+
return applyVersionedMutations(session.version, session.script, source, operations);
|
|
660
|
+
}
|
|
661
|
+
function v4Counts(script) {
|
|
662
|
+
return {
|
|
663
|
+
episodes: script.episodes.length,
|
|
664
|
+
scenes: script.episodes.reduce((sum, episode) => sum + episode.scenes.length, 0),
|
|
665
|
+
actions: script.episodes.reduce((sum, episode) => sum + episode.scenes.reduce((sceneSum, scene) => sceneSum + scene.actions.length, 0), 0),
|
|
666
|
+
actors: script.actors.length,
|
|
667
|
+
locations: script.locations.length,
|
|
668
|
+
props: script.props.length,
|
|
669
|
+
states: [...script.actors, ...script.locations, ...script.props]
|
|
670
|
+
.reduce((sum, asset) => sum + asset.states.length, 0),
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function v4CanonicalIds(script) {
|
|
674
|
+
return [
|
|
675
|
+
...script.actors.flatMap((asset) => [
|
|
676
|
+
asset.actor_id,
|
|
677
|
+
...asset.states.map((state) => `${asset.actor_id}/${state.state_id}`),
|
|
678
|
+
]),
|
|
679
|
+
...script.locations.flatMap((asset) => [
|
|
680
|
+
asset.location_id,
|
|
681
|
+
...asset.states.map((state) => `${asset.location_id}/${state.state_id}`),
|
|
682
|
+
]),
|
|
683
|
+
...script.props.flatMap((asset) => [
|
|
684
|
+
asset.prop_id,
|
|
685
|
+
...asset.states.map((state) => `${asset.prop_id}/${state.state_id}`),
|
|
686
|
+
]),
|
|
687
|
+
...script.episodes.flatMap((episode) => [
|
|
688
|
+
episode.episode_id,
|
|
689
|
+
...episode.scenes.flatMap((scene) => [
|
|
690
|
+
scene.scene_id,
|
|
691
|
+
...scene.actions.map((action) => action.action_id),
|
|
692
|
+
]),
|
|
693
|
+
]),
|
|
694
|
+
];
|
|
695
|
+
}
|
|
696
|
+
function orderedIntersection(left, right) {
|
|
697
|
+
return left.filter((value) => right.has(value));
|
|
698
|
+
}
|
|
699
|
+
function v4OperationTargets(operation) {
|
|
700
|
+
const values = [];
|
|
701
|
+
const isIdentity = (value) => {
|
|
702
|
+
if (value === "global")
|
|
703
|
+
return true;
|
|
704
|
+
if (isV4Id("episode", value)
|
|
705
|
+
|| isV4Id("scene", value)
|
|
706
|
+
|| isV4Id("action", value)
|
|
707
|
+
|| isV4Id("actor", value)
|
|
708
|
+
|| isV4Id("location", value)
|
|
709
|
+
|| isV4Id("prop", value))
|
|
710
|
+
return true;
|
|
711
|
+
const slash = value.indexOf("/");
|
|
712
|
+
if (slash < 0 || slash !== value.lastIndexOf("/"))
|
|
713
|
+
return false;
|
|
714
|
+
const owner = value.slice(0, slash);
|
|
715
|
+
const state = value.slice(slash + 1);
|
|
716
|
+
return (isV4Id("actor", owner)
|
|
717
|
+
|| isV4Id("location", owner)
|
|
718
|
+
|| isV4Id("prop", owner)) && (state === "default" || isV4Id("state", state));
|
|
719
|
+
};
|
|
720
|
+
const add = (value) => {
|
|
721
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
722
|
+
return;
|
|
723
|
+
const normalized = value.trim();
|
|
724
|
+
if (isIdentity(normalized))
|
|
725
|
+
values.push(normalized);
|
|
726
|
+
};
|
|
727
|
+
const first = (...fields) => {
|
|
728
|
+
for (const field of fields) {
|
|
729
|
+
const value = operation[field];
|
|
730
|
+
if (typeof value === "string" && value.trim() !== "")
|
|
731
|
+
return value;
|
|
732
|
+
}
|
|
733
|
+
return undefined;
|
|
734
|
+
};
|
|
735
|
+
const addState = (ownerValue, stateValue) => {
|
|
736
|
+
const owner = strOf(ownerValue).trim();
|
|
737
|
+
const state = strOf(stateValue).trim();
|
|
738
|
+
if (owner && state)
|
|
739
|
+
add(`${owner}/${state}`);
|
|
740
|
+
};
|
|
741
|
+
const addStates = (ownerValue, stateValues) => {
|
|
742
|
+
if (!Array.isArray(stateValues))
|
|
743
|
+
return;
|
|
744
|
+
stateValues.forEach((state) => addState(ownerValue, state));
|
|
745
|
+
};
|
|
746
|
+
const addPlacement = () => {
|
|
747
|
+
add(operation["before"]);
|
|
748
|
+
add(operation["after"]);
|
|
749
|
+
};
|
|
750
|
+
const kind = strOf(operation["op"]);
|
|
751
|
+
switch (kind) {
|
|
752
|
+
case "action.parenthetical.set":
|
|
753
|
+
case "action.parenthetical.remove":
|
|
754
|
+
case "action.content.replace":
|
|
755
|
+
case "action.content.regex-replace":
|
|
756
|
+
case "action.type.set":
|
|
757
|
+
case "action.delete":
|
|
758
|
+
case "action.transition.clear":
|
|
759
|
+
add(operation["at"]);
|
|
760
|
+
break;
|
|
761
|
+
case "action.actor.set":
|
|
762
|
+
case "dialogue.speaker.set":
|
|
763
|
+
add(operation["at"]);
|
|
764
|
+
add(operation["actor_id"]);
|
|
765
|
+
addState(operation["actor_id"], operation["state_id"]);
|
|
766
|
+
break;
|
|
767
|
+
case "action.insert":
|
|
768
|
+
add(operation["at"]);
|
|
769
|
+
add(first("action_id", "id"));
|
|
770
|
+
add(operation["actor_id"]);
|
|
771
|
+
addState(operation["actor_id"], operation["state_id"]);
|
|
772
|
+
addPlacement();
|
|
773
|
+
break;
|
|
774
|
+
case "action.move":
|
|
775
|
+
add(operation["at"]);
|
|
776
|
+
add(operation["to"]);
|
|
777
|
+
addPlacement();
|
|
778
|
+
break;
|
|
779
|
+
case "action.transition.set":
|
|
780
|
+
add(operation["at"]);
|
|
781
|
+
add(operation["target"]);
|
|
782
|
+
break;
|
|
783
|
+
case "scene.ref.set":
|
|
784
|
+
case "scene.ref.remove":
|
|
785
|
+
add(operation["at"]);
|
|
786
|
+
add(operation["target"]);
|
|
787
|
+
addStates(operation["target"], operation["state_ids"]);
|
|
788
|
+
break;
|
|
789
|
+
case "actor.add":
|
|
790
|
+
add(first("actor_id", "id"));
|
|
791
|
+
break;
|
|
792
|
+
case "location.add":
|
|
793
|
+
add(first("location_id", "id"));
|
|
794
|
+
break;
|
|
795
|
+
case "prop.add":
|
|
796
|
+
add(first("prop_id", "id"));
|
|
797
|
+
break;
|
|
798
|
+
case "state.add": {
|
|
799
|
+
const owner = operation["target"];
|
|
800
|
+
add(owner);
|
|
801
|
+
addState(owner, first("state_id", "id"));
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
case "state.rename":
|
|
805
|
+
case "state.describe":
|
|
806
|
+
add(operation["target"]);
|
|
807
|
+
break;
|
|
808
|
+
case "state.delete": {
|
|
809
|
+
const target = strOf(operation["target"]).trim();
|
|
810
|
+
add(target);
|
|
811
|
+
const slash = target.indexOf("/");
|
|
812
|
+
if (slash > 0)
|
|
813
|
+
addState(target.slice(0, slash), operation["replacement"]);
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
case "asset.rename":
|
|
817
|
+
case "asset.describe":
|
|
818
|
+
case "asset.role.set":
|
|
819
|
+
case "asset.alias.set":
|
|
820
|
+
case "asset.alias.add":
|
|
821
|
+
case "asset.alias.remove":
|
|
822
|
+
case "importance.set":
|
|
823
|
+
add(operation["target"]);
|
|
824
|
+
break;
|
|
825
|
+
case "asset.merge":
|
|
826
|
+
add(first("from", "source"));
|
|
827
|
+
add(first("into", "target"));
|
|
828
|
+
break;
|
|
829
|
+
case "asset.delete":
|
|
830
|
+
add(operation["target"]);
|
|
831
|
+
if (operation["strategy"] === "replace")
|
|
832
|
+
add(operation["replacement"]);
|
|
833
|
+
break;
|
|
834
|
+
case "episode.add":
|
|
835
|
+
add(first("episode_id", "id"));
|
|
836
|
+
break;
|
|
837
|
+
case "episode.synopsis.set":
|
|
838
|
+
add(first("episode_id", "target", "at"));
|
|
839
|
+
break;
|
|
840
|
+
case "meta.worldview.set":
|
|
841
|
+
case "meta.synopsis.set":
|
|
842
|
+
add("global");
|
|
843
|
+
break;
|
|
844
|
+
case "scene.insert": {
|
|
845
|
+
const location = first("location", "location_id");
|
|
846
|
+
add(first("at", "episode_id"));
|
|
847
|
+
add(first("scene_id", "id"));
|
|
848
|
+
add(location);
|
|
849
|
+
addStates(location, operation["location_state_ids"]);
|
|
850
|
+
addPlacement();
|
|
851
|
+
break;
|
|
852
|
+
}
|
|
853
|
+
case "scene.move":
|
|
854
|
+
add(operation["at"]);
|
|
855
|
+
add(operation["to"]);
|
|
856
|
+
addPlacement();
|
|
857
|
+
break;
|
|
858
|
+
case "scene.delete":
|
|
859
|
+
add(operation["at"]);
|
|
860
|
+
break;
|
|
861
|
+
case "scene.split":
|
|
862
|
+
add(operation["at"]);
|
|
863
|
+
add(first("at_action", "action_id"));
|
|
864
|
+
add(first("new_scene_id", "scene_id"));
|
|
865
|
+
break;
|
|
866
|
+
case "scene.merge":
|
|
867
|
+
add(operation["from"]);
|
|
868
|
+
add(operation["into"]);
|
|
869
|
+
break;
|
|
870
|
+
case "extend.set":
|
|
871
|
+
case "extend.clear":
|
|
872
|
+
add(operation["at"]);
|
|
873
|
+
break;
|
|
874
|
+
default:
|
|
875
|
+
throw new Error(`V4 plan target mapping is missing for operation ${kind}`);
|
|
876
|
+
}
|
|
877
|
+
return [...new Set(values)];
|
|
878
|
+
}
|
|
879
|
+
export function buildV4MutationPlan(session, operations, options = {}) {
|
|
880
|
+
if (session.version !== 4) {
|
|
881
|
+
throw new CliError("A v4 mutation plan requires Script v4.", {
|
|
882
|
+
exitCode: EXIT_USAGE,
|
|
883
|
+
errorCode: "V4_PLAN_VERSION_INVALID",
|
|
884
|
+
data: { actual: [`Script v${session.version}`] },
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
const original = requireScriptV4(session.script);
|
|
888
|
+
const proposed = structuredClone(original);
|
|
889
|
+
const originalIds = v4CanonicalIds(original);
|
|
890
|
+
const applied = [];
|
|
891
|
+
const steps = [];
|
|
892
|
+
for (const [index, operation] of operations.entries()) {
|
|
893
|
+
const beforeIds = v4CanonicalIds(proposed);
|
|
894
|
+
const beforeSet = new Set(beforeIds);
|
|
895
|
+
const before = v4Counts(proposed);
|
|
896
|
+
let status = "applied";
|
|
897
|
+
try {
|
|
898
|
+
applied.push(...applyPatchOperationsV4(proposed, [operation]));
|
|
899
|
+
}
|
|
900
|
+
catch (error) {
|
|
901
|
+
if (error instanceof CliError
|
|
902
|
+
&& error.errorCode
|
|
903
|
+
&& options.skipCodes?.has(error.errorCode)) {
|
|
904
|
+
status = "skipped";
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
throw error;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const afterIds = v4CanonicalIds(proposed);
|
|
911
|
+
const afterSet = new Set(afterIds);
|
|
912
|
+
steps.push({
|
|
913
|
+
index,
|
|
914
|
+
operation,
|
|
915
|
+
targets: v4OperationTargets(operation),
|
|
916
|
+
status,
|
|
917
|
+
before,
|
|
918
|
+
after: v4Counts(proposed),
|
|
919
|
+
created_ids: afterIds.filter((id) => !beforeSet.has(id)),
|
|
920
|
+
removed_ids: beforeIds.filter((id) => !afterSet.has(id)),
|
|
921
|
+
retained_ids: orderedIntersection(beforeIds, afterSet),
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
const proposedIds = v4CanonicalIds(proposed);
|
|
925
|
+
const originalSet = new Set(originalIds);
|
|
926
|
+
const proposedSet = new Set(proposedIds);
|
|
927
|
+
const targets = [...new Set(steps.flatMap((step) => step.targets))];
|
|
928
|
+
const touched = new Set([
|
|
929
|
+
...targets,
|
|
930
|
+
...steps.flatMap((step) => step.created_ids),
|
|
931
|
+
...steps.flatMap((step) => step.removed_ids),
|
|
932
|
+
]);
|
|
933
|
+
const plan = {
|
|
934
|
+
version: 1,
|
|
935
|
+
script_version: 4,
|
|
936
|
+
baseline: session.baselineHash,
|
|
937
|
+
operations,
|
|
938
|
+
targets,
|
|
939
|
+
steps,
|
|
940
|
+
before: v4Counts(original),
|
|
941
|
+
after: v4Counts(proposed),
|
|
942
|
+
created_ids: proposedIds.filter((id) => !originalSet.has(id)),
|
|
943
|
+
removed_ids: originalIds.filter((id) => !proposedSet.has(id)),
|
|
944
|
+
retained_ids: orderedIntersection(originalIds, proposedSet),
|
|
945
|
+
untouched_ids: orderedIntersection(originalIds, proposedSet)
|
|
946
|
+
.filter((id) => !touched.has(id)),
|
|
947
|
+
validation: validateScriptV4(proposed),
|
|
948
|
+
};
|
|
949
|
+
return {
|
|
950
|
+
plan,
|
|
951
|
+
digest: `sha256:${sha256Text(JSON.stringify(sortDeep(plan)))}`,
|
|
952
|
+
applied,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
const V4_PLANNED_ID_FIELDS = {
|
|
956
|
+
"actor.add": "actor_id",
|
|
957
|
+
"location.add": "location_id",
|
|
958
|
+
"prop.add": "prop_id",
|
|
959
|
+
"state.add": "state_id",
|
|
960
|
+
"episode.add": "episode_id",
|
|
961
|
+
"scene.insert": "scene_id",
|
|
962
|
+
"scene.split": "new_scene_id",
|
|
963
|
+
"action.insert": "action_id",
|
|
964
|
+
};
|
|
965
|
+
export function assertV4PlanHasExplicitNewIds(operations) {
|
|
966
|
+
for (const operation of operations) {
|
|
967
|
+
const kind = strOf(operation["op"]);
|
|
968
|
+
const field = V4_PLANNED_ID_FIELDS[kind];
|
|
969
|
+
if (!field || strOf(operation[field]).trim())
|
|
970
|
+
continue;
|
|
971
|
+
const idKind = field === "new_scene_id" ? "scene" : field.replace(/_id$/, "");
|
|
972
|
+
throw new CliError("Planned creation operations require an explicit candidate ID.", {
|
|
973
|
+
exitCode: EXIT_USAGE,
|
|
974
|
+
errorCode: "V4_PLAN_ID_REQUIRED",
|
|
975
|
+
hint: `Generate a candidate with \`scriptctl ids --kind ${idKind} --count 1\`, then include it in the operation.`,
|
|
976
|
+
data: { expected: [`${kind}.${field}`], actual: [`${kind}.${field}=<missing>`] },
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
}
|
|
522
980
|
// ---------------------------------------------------------------------------
|
|
523
981
|
// Single-op helpers
|
|
524
982
|
// ---------------------------------------------------------------------------
|
|
525
|
-
export async function
|
|
983
|
+
export async function applySingleScriptOpToSession(session, opts, op) {
|
|
984
|
+
const plan = strOf(opts["plan"]).trim();
|
|
985
|
+
if (plan) {
|
|
986
|
+
throw new CliError("Single-point writes do not use plan digests.", {
|
|
987
|
+
exitCode: EXIT_USAGE,
|
|
988
|
+
errorCode: "MULTI_PLAN_USAGE_INVALID",
|
|
989
|
+
hint: "Remove --plan from this single-point edit.",
|
|
990
|
+
data: { actual: [plan] },
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
return applyScriptOpsToSession(session, opts, [op]);
|
|
994
|
+
}
|
|
995
|
+
export async function applyScriptOpsToSession(session, opts, operations) {
|
|
526
996
|
const workspace = strOf(opts["workspace_path"] || "workspace");
|
|
527
|
-
const
|
|
528
|
-
const
|
|
529
|
-
const applied = applyPatchOperations(script, scriptSourceText(workspace), [op]);
|
|
530
|
-
const opName = applied[0] ?? strOf(op["op"]);
|
|
997
|
+
const applied = applyPatchOperationsForSession(session, scriptSourceText(workspace), operations);
|
|
998
|
+
const opName = applied.length === 1 ? applied[0] : "script.ops";
|
|
531
999
|
const [newRevision, idempotent] = await saveScriptSession(session, opts, opName);
|
|
532
1000
|
const validation = validateSession(session);
|
|
533
1001
|
await syncValidationResult(session, validation, newRevision);
|
|
534
1002
|
const passed = Boolean(validation["passed"]);
|
|
535
1003
|
// One outcome line: what changed + revision (remote) + validation verdict.
|
|
536
1004
|
const rev = session.remote ? `; rev ${newRevision}` : "";
|
|
537
|
-
const resultLine = `${
|
|
1005
|
+
const resultLine = `${applied.join(", ")} applied${rev}; validation ${passed ? "passed" : "needs repair"}`;
|
|
1006
|
+
const issues = summarizeIssues(asList(validation["issues"]));
|
|
538
1007
|
const report = {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
1008
|
+
text: [resultLine, ...issues].join("\n"),
|
|
1009
|
+
data: {
|
|
1010
|
+
op: opName,
|
|
1011
|
+
changed: applied.length > 0,
|
|
1012
|
+
revision: session.remote ? newRevision : undefined,
|
|
1013
|
+
idempotent: session.remote ? idempotent : undefined,
|
|
1014
|
+
validation,
|
|
1015
|
+
issues,
|
|
1016
|
+
script_path: session.artifactLabel,
|
|
1017
|
+
},
|
|
549
1018
|
};
|
|
550
1019
|
return [report, passed ? EXIT_OK : EXIT_NEEDS_AGENT];
|
|
551
1020
|
}
|
|
1021
|
+
export async function applySingleScriptOp(opts, op) {
|
|
1022
|
+
const session = await loadScriptForEdit(opts);
|
|
1023
|
+
return applySingleScriptOpToSession(session, opts, op);
|
|
1024
|
+
}
|
|
552
1025
|
export function summarizeScriptOp(_script, op) {
|
|
553
1026
|
const kind = strOf(op["op"]);
|
|
554
1027
|
if (kind === "actor.add")
|
|
555
|
-
return
|
|
1028
|
+
return `Added actor ${op["name"] || op["actor_name"]}${op["role_type"] ? ` (${op["role_type"]})` : ""}`;
|
|
556
1029
|
if (kind === "location.add")
|
|
557
|
-
return
|
|
1030
|
+
return `Added location ${op["name"] || op["location_name"]}`;
|
|
558
1031
|
if (kind === "prop.add")
|
|
559
|
-
return
|
|
1032
|
+
return `Added prop ${op["name"] || op["prop_name"]}`;
|
|
560
1033
|
if (kind === "episode.add")
|
|
561
|
-
return
|
|
1034
|
+
return `Added episode${op["episode_id"] ? ` ${op["episode_id"]}` : " (generated ID)"}${op["title"] ? `: ${op["title"]}` : ""}`;
|
|
562
1035
|
if (kind === "meta.synopsis.set")
|
|
563
|
-
return
|
|
1036
|
+
return "Updated script synopsis";
|
|
564
1037
|
if (kind === "episode.synopsis.set")
|
|
565
|
-
return
|
|
1038
|
+
return `Updated episode synopsis ${op["episode_id"] ?? op["target"] ?? op["at"]}`;
|
|
566
1039
|
if (kind === "state.add")
|
|
567
|
-
return
|
|
1040
|
+
return `Added state ${op["state_id"]}: ${op["name"] || op["state_name"]} to ${op["target"]}`;
|
|
568
1041
|
if (kind === "importance.set")
|
|
569
|
-
return
|
|
1042
|
+
return `Set ${op["target"]} importance to ${op["importance"]}`;
|
|
570
1043
|
if (kind === "scene.ref.set")
|
|
571
|
-
return
|
|
1044
|
+
return `Set reference ${op["target"]} on ${op["at"]}${op["state"] ? ` (state ${op["state"]})` : ""}`;
|
|
572
1045
|
if (kind === "extend.set")
|
|
573
|
-
return
|
|
1046
|
+
return `Set extend.${op["key"]} on ${op["at"]}`;
|
|
574
1047
|
if (kind === "extend.clear")
|
|
575
|
-
return op["key"] ?
|
|
1048
|
+
return op["key"] ? `Removed extend.${op["key"]} from ${op["at"]}` : `Cleared extend on ${op["at"]}`;
|
|
576
1049
|
if (kind.startsWith("state."))
|
|
577
|
-
return
|
|
1050
|
+
return `Applied ${kind}: ${op["target"]}`;
|
|
578
1051
|
if (kind.startsWith("dialogue."))
|
|
579
|
-
return
|
|
580
|
-
return
|
|
1052
|
+
return `Applied ${kind}: ${op["at"]}`;
|
|
1053
|
+
return `Applied ${kind}`;
|
|
581
1054
|
}
|
|
582
1055
|
// Accept `/re/flags` as-is; wrap a bare pattern as `/pattern/` so
|
|
583
1056
|
// `replace --regex --from '^\s*\[[^\]]*\]'` works without the user typing slashes.
|
|
@@ -593,61 +1066,129 @@ export function regexPattern(raw) {
|
|
|
593
1066
|
// reports the blast radius and writes nothing; --apply commits. Per-action
|
|
594
1067
|
// "no match here / overlap dialogue" errors are skipped (a sweep); anything else
|
|
595
1068
|
// aborts the whole edit before any write. Shared by every selector-capable verb.
|
|
596
|
-
export async function
|
|
1069
|
+
export async function applySelectorEditsToSession(session, opts, template, verbLabel) {
|
|
597
1070
|
const workspace = strOf(opts["workspace_path"] || "workspace");
|
|
598
1071
|
const apply = Boolean(opts["apply"]);
|
|
599
1072
|
const opName = strOf(template["op"]);
|
|
600
|
-
const session = await loadScriptForEdit(opts);
|
|
601
1073
|
const script = session.script;
|
|
602
|
-
const
|
|
603
|
-
|
|
1074
|
+
const refs = session.version === 3
|
|
1075
|
+
? resolveActionTargets(script, opts).map((target) => `${target.epId}/${target.sceneId}#${target.idx}`)
|
|
1076
|
+
: resolveActionTargetsV4(requireScriptV4(script), opts, { allowOrdinals: false })
|
|
1077
|
+
.map((target) => target.action.action_id);
|
|
1078
|
+
const operations = refs.map((ref) => ({ ...template, at: ref }));
|
|
1079
|
+
const multiV4 = session.version === 4 && refs.length > 1;
|
|
1080
|
+
const planned = multiV4
|
|
1081
|
+
? buildV4MutationPlan(session, operations, { skipCodes: SKIPPABLE_EDIT_CODES })
|
|
1082
|
+
: null;
|
|
1083
|
+
const planDigest = planned?.digest ?? null;
|
|
1084
|
+
const suppliedPlan = strOf(opts["plan"]).trim();
|
|
1085
|
+
if (suppliedPlan && (!multiV4 || !apply)) {
|
|
1086
|
+
throw new CliError("A plan digest is only accepted for a v4 multi-target apply.", {
|
|
1087
|
+
exitCode: EXIT_USAGE,
|
|
1088
|
+
errorCode: "MULTI_PLAN_USAGE_INVALID",
|
|
1089
|
+
hint: "Remove --plan, or apply the same v4 multi-target selection that produced the digest.",
|
|
1090
|
+
data: { actual: [suppliedPlan] },
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
if (multiV4 && apply && !suppliedPlan) {
|
|
1094
|
+
throw new CliError("A v4 multi-target write requires its dry-run plan digest.", {
|
|
1095
|
+
exitCode: EXIT_USAGE,
|
|
1096
|
+
errorCode: "MULTI_PLAN_REQUIRED",
|
|
1097
|
+
hint: "Run the same command without --apply, review the full plan, then retry with `--apply --plan <digest>`.",
|
|
1098
|
+
data: { expected: [planDigest ?? ""], actual: ["<missing>"] },
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
if (multiV4 && apply && suppliedPlan !== planDigest) {
|
|
1102
|
+
throw new CliError("The supplied digest does not match the current targets and operations.", {
|
|
1103
|
+
exitCode: EXIT_INPUT,
|
|
1104
|
+
errorCode: "MULTI_PLAN_MISMATCH",
|
|
1105
|
+
hint: "Review the newly computed dry-run plan, then apply its exact digest.",
|
|
1106
|
+
data: { expected: [planDigest ?? ""], actual: [suppliedPlan] },
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
604
1109
|
const source = scriptSourceText(workspace);
|
|
605
|
-
let applied = 0;
|
|
606
|
-
let skipped = 0;
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
1110
|
+
let applied = planned?.plan.steps.filter((step) => step.status === "applied").length ?? 0;
|
|
1111
|
+
let skipped = planned?.plan.steps.filter((step) => step.status === "skipped").length ?? 0;
|
|
1112
|
+
if (!planned) {
|
|
1113
|
+
for (const ref of refs) {
|
|
1114
|
+
try {
|
|
1115
|
+
applyPatchOperationsForSession(session, source, [{ ...template, at: ref }]);
|
|
1116
|
+
applied += 1;
|
|
1117
|
+
}
|
|
1118
|
+
catch (err) {
|
|
1119
|
+
if (err instanceof CliError && err.errorCode && SKIPPABLE_EDIT_CODES.has(err.errorCode)) {
|
|
1120
|
+
skipped += 1;
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
throw err;
|
|
1124
|
+
}
|
|
611
1125
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
1126
|
+
}
|
|
1127
|
+
else if (apply) {
|
|
1128
|
+
for (const step of planned.plan.steps) {
|
|
1129
|
+
if (step.status === "applied") {
|
|
1130
|
+
applyPatchOperationsForSession(session, source, [step.operation]);
|
|
616
1131
|
}
|
|
617
|
-
throw err;
|
|
618
1132
|
}
|
|
619
1133
|
}
|
|
620
|
-
const
|
|
1134
|
+
const displayedRefs = multiV4 ? refs : refs.slice(0, 12);
|
|
621
1135
|
const blast = [
|
|
622
1136
|
`verb: ${verbLabel} (${opName})`,
|
|
623
1137
|
`matched: ${refs.length} action(s)`,
|
|
624
1138
|
`${apply ? "applied" : "would-apply"}: ${applied}`,
|
|
625
1139
|
`${apply ? "skipped" : "would-skip"}: ${skipped}`,
|
|
626
|
-
...(
|
|
1140
|
+
...(displayedRefs.length ? [`targets: ${displayedRefs.join(", ")}${refs.length > displayedRefs.length ? ` … (+${refs.length - displayedRefs.length} more)` : ""}`] : []),
|
|
1141
|
+
...(planDigest ? [`plan_digest: ${planDigest}`] : []),
|
|
627
1142
|
];
|
|
628
1143
|
if (!apply) {
|
|
629
|
-
const
|
|
630
|
-
const passed =
|
|
1144
|
+
const legacyValidation = planned ? null : validateSession(session);
|
|
1145
|
+
const passed = planned
|
|
1146
|
+
? planned.plan.validation.ok
|
|
1147
|
+
: Boolean(legacyValidation?.["passed"]);
|
|
1148
|
+
const plannedIssueLines = planned
|
|
1149
|
+
? [...planned.plan.validation.errors, ...planned.plan.validation.warnings]
|
|
1150
|
+
.map((issue) => `${issue.path}: ${issue.code} - ${issue.message}`)
|
|
1151
|
+
: null;
|
|
1152
|
+
const legacyIssueLines = legacyValidation === null
|
|
1153
|
+
? []
|
|
1154
|
+
: summarizeIssues(asList(legacyValidation["issues"]));
|
|
1155
|
+
const issues = plannedIssueLines ?? legacyIssueLines;
|
|
1156
|
+
const hint = refs.length === 0
|
|
1157
|
+
? "Widen the selection with --in, --grep, or predicates."
|
|
1158
|
+
: planDigest
|
|
1159
|
+
? `Run again with --apply --plan ${planDigest} to commit this exact plan.`
|
|
1160
|
+
: "Run again with --apply to commit.";
|
|
1161
|
+
const lines = [...blast, `validation: ${passed ? "would pass" : "would need repair"}; no write`, ...issues, hint];
|
|
631
1162
|
const report = {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
1163
|
+
text: lines.join("\n"),
|
|
1164
|
+
data: {
|
|
1165
|
+
op: `${opName}.dry-run`,
|
|
1166
|
+
changed: false,
|
|
1167
|
+
matched: refs.length,
|
|
1168
|
+
applied,
|
|
1169
|
+
skipped,
|
|
1170
|
+
issues,
|
|
1171
|
+
script_path: session.artifactLabel,
|
|
1172
|
+
...(planDigest ? {
|
|
1173
|
+
baseline: session.baselineHash,
|
|
1174
|
+
plan_digest: planDigest,
|
|
1175
|
+
operations,
|
|
1176
|
+
targets: refs,
|
|
1177
|
+
plan: planned?.plan,
|
|
1178
|
+
} : {}),
|
|
1179
|
+
},
|
|
640
1180
|
};
|
|
641
1181
|
return [report, passed ? EXIT_OK : EXIT_NEEDS_AGENT];
|
|
642
1182
|
}
|
|
643
1183
|
if (refs.length === 0) {
|
|
644
1184
|
return [{
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
1185
|
+
text: [...blast, "Widen the selection with --in, --grep, or predicates."].join("\n"),
|
|
1186
|
+
data: {
|
|
1187
|
+
op: opName,
|
|
1188
|
+
changed: false,
|
|
1189
|
+
matched: 0,
|
|
1190
|
+
script_path: session.artifactLabel,
|
|
1191
|
+
},
|
|
651
1192
|
}, EXIT_OK];
|
|
652
1193
|
}
|
|
653
1194
|
const [newRevision, idempotent] = await saveScriptSession(session, opts, opName);
|
|
@@ -655,26 +1196,42 @@ export async function applySelectorEdits(opts, template, verbLabel) {
|
|
|
655
1196
|
await syncValidationResult(session, validation, newRevision);
|
|
656
1197
|
const passed = Boolean(validation["passed"]);
|
|
657
1198
|
const rev = session.remote ? `; rev ${newRevision}` : "";
|
|
1199
|
+
const issues = summarizeIssues(asList(validation["issues"]));
|
|
658
1200
|
const report = {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
1201
|
+
text: [...blast, `validation: ${passed ? "passed" : "needs repair"}${rev}`, ...issues].join("\n"),
|
|
1202
|
+
data: {
|
|
1203
|
+
op: opName,
|
|
1204
|
+
changed: applied > 0,
|
|
1205
|
+
matched: refs.length,
|
|
1206
|
+
applied,
|
|
1207
|
+
skipped,
|
|
1208
|
+
revision: session.remote ? newRevision : undefined,
|
|
1209
|
+
idempotent: session.remote ? idempotent : undefined,
|
|
1210
|
+
validation,
|
|
1211
|
+
issues,
|
|
1212
|
+
script_path: session.artifactLabel,
|
|
1213
|
+
...(planDigest ? {
|
|
1214
|
+
baseline: session.baselineHash,
|
|
1215
|
+
plan_digest: planDigest,
|
|
1216
|
+
operations,
|
|
1217
|
+
targets: refs,
|
|
1218
|
+
plan: planned?.plan,
|
|
1219
|
+
} : {}),
|
|
1220
|
+
},
|
|
668
1221
|
};
|
|
669
1222
|
return [report, passed ? EXIT_OK : EXIT_NEEDS_AGENT];
|
|
670
1223
|
}
|
|
1224
|
+
export async function applySelectorEdits(opts, template, verbLabel) {
|
|
1225
|
+
const session = await loadScriptForEdit(opts);
|
|
1226
|
+
return applySelectorEditsToSession(session, opts, template, verbLabel);
|
|
1227
|
+
}
|
|
671
1228
|
export { sortDeep };
|
|
672
1229
|
// ===========================================================================
|
|
673
1230
|
// 0.6.0 plural-noun query commands (replace `commandScriptInspect --target X`)
|
|
674
1231
|
// ===========================================================================
|
|
675
1232
|
//
|
|
676
1233
|
// Each command is a thin read-only wrapper around the underlying script
|
|
677
|
-
// document. They all return a
|
|
1234
|
+
// document. They all return a CommandOutput shaped like inspect's existing output but
|
|
678
1235
|
// without the `--target` flag — the verb itself encodes the target. Filters
|
|
679
1236
|
// use `--in <addr>` (compose with the address parsers) plus per-target
|
|
680
1237
|
// predicates (--name / --kind / --has / --grep / --context / --type / --actor
|
|
@@ -725,17 +1282,19 @@ export function buildGrepMatcher(raw) {
|
|
|
725
1282
|
if (m) {
|
|
726
1283
|
const pattern = m[1];
|
|
727
1284
|
if (isDangerousRegex(pattern)) {
|
|
728
|
-
throw new CliError("
|
|
1285
|
+
throw new CliError("regex pattern has nested quantifiers (ReDoS risk) or exceeds the length limit.", {
|
|
729
1286
|
exitCode: EXIT_USAGE,
|
|
730
|
-
required: [`pattern length <= ${MAX_GREP_PATTERN_LEN}; no quantifier-on-group-with-internal-quantifier (e.g. (a+)+)`],
|
|
731
|
-
received: [`length=${pattern.length}, sample=${pattern.slice(0, 80)}`],
|
|
732
|
-
nextSteps: ["Rewrite the regex to avoid nested quantifiers, or use a literal substring (drop the /…/ wrapper)."],
|
|
733
1287
|
errorCode: "REGEX_UNSAFE",
|
|
1288
|
+
hint: "Rewrite the regex to avoid nested quantifiers, or use a literal substring (drop the /…/ wrapper).",
|
|
1289
|
+
data: { expected: [`pattern length <= ${MAX_GREP_PATTERN_LEN}; no quantifier-on-group-with-internal-quantifier (e.g. (a+)+)`], actual: [`length=${pattern.length}, sample=${pattern.slice(0, 80)}`] },
|
|
734
1290
|
});
|
|
735
1291
|
}
|
|
736
1292
|
try {
|
|
737
1293
|
const re = new RegExp(pattern, m[2]);
|
|
738
|
-
return (hay) =>
|
|
1294
|
+
return (hay) => {
|
|
1295
|
+
re.lastIndex = 0;
|
|
1296
|
+
return re.test(hay);
|
|
1297
|
+
};
|
|
739
1298
|
}
|
|
740
1299
|
catch {
|
|
741
1300
|
// Malformed regex — treat as literal.
|
|
@@ -743,15 +1302,22 @@ export function buildGrepMatcher(raw) {
|
|
|
743
1302
|
}
|
|
744
1303
|
return (hay) => hay.includes(raw);
|
|
745
1304
|
}
|
|
746
|
-
export function buildReport(op,
|
|
1305
|
+
export function buildReport(op, lines) {
|
|
1306
|
+
const outputLines = lines.length > 0 ? lines : ["No matches."];
|
|
747
1307
|
return {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1308
|
+
text: outputLines.join("\n"),
|
|
1309
|
+
data: {
|
|
1310
|
+
op,
|
|
1311
|
+
changed: false,
|
|
1312
|
+
},
|
|
753
1313
|
};
|
|
754
1314
|
}
|
|
1315
|
+
export function buildTableReport(op, lines, rowColumns, rows) {
|
|
1316
|
+
const report = buildReport(op, lines);
|
|
1317
|
+
report.data["row_columns"] = rowColumns;
|
|
1318
|
+
report.data["rows"] = rows;
|
|
1319
|
+
return report;
|
|
1320
|
+
}
|
|
755
1321
|
// ----- summary --------------------------------------------------------------
|
|
756
1322
|
export function synopsisPreview(text, limit) {
|
|
757
1323
|
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
@@ -815,12 +1381,11 @@ export function resolveActionTargets(script, opts) {
|
|
|
815
1381
|
// "state_changes" (underscore) passed the filter-missing guard but bypassed
|
|
816
1382
|
// every concrete predicate, returning the whole script.
|
|
817
1383
|
if (hasFilter && !HAS_FILTERS.has(hasFilter)) {
|
|
818
|
-
throw new CliError("
|
|
1384
|
+
throw new CliError("--has value invalid.", {
|
|
819
1385
|
exitCode: EXIT_USAGE,
|
|
820
|
-
required: [`--has: one of ${[...HAS_FILTERS].join(", ")}`],
|
|
821
|
-
received: [hasFilter],
|
|
822
|
-
nextSteps: ["Use a supported --has value."],
|
|
823
1386
|
errorCode: "HAS_FILTER_INVALID",
|
|
1387
|
+
hint: "Use a supported --has value.",
|
|
1388
|
+
data: { expected: [`--has: one of ${[...HAS_FILTERS].join(", ")}`], actual: [hasFilter] },
|
|
824
1389
|
});
|
|
825
1390
|
}
|
|
826
1391
|
const grepMatcher = buildGrepMatcher(strOf(opts["grep"]));
|
|
@@ -854,7 +1419,7 @@ export function resolveActionTargets(script, opts) {
|
|
|
854
1419
|
}
|
|
855
1420
|
// Whether a selection in `opts` has any filter at all (scope or predicate).
|
|
856
1421
|
export function selectionHasAnyFilter(opts) {
|
|
857
|
-
return Boolean(
|
|
1422
|
+
return Boolean(strOf(opts["in"]).trim() ||
|
|
858
1423
|
strOf(opts["grep"]) || strOf(opts["type"]).trim() || strOf(opts["actor"]).trim() ||
|
|
859
1424
|
strOf(opts["has"]).trim());
|
|
860
1425
|
}
|
|
@@ -913,6 +1478,7 @@ export function assetAppearanceCounts(script, kind) {
|
|
|
913
1478
|
}
|
|
914
1479
|
export function listAssetsByKind(script, kind, opts) {
|
|
915
1480
|
const lines = [];
|
|
1481
|
+
const rows = [];
|
|
916
1482
|
const [key, idKey, nameKey] = (() => {
|
|
917
1483
|
switch (kind) {
|
|
918
1484
|
case "actor": return ["actors", "actor_id", "actor_name"];
|
|
@@ -989,8 +1555,42 @@ export function listAssetsByKind(script, kind, opts) {
|
|
|
989
1555
|
extra.push(`scenes=${scns}`, `episodes_count=${eps}${arc}`);
|
|
990
1556
|
}
|
|
991
1557
|
lines.push(`${kind} ${id}: ${name || "-"} ${extra.join(" ")}`);
|
|
1558
|
+
const seen = appearances?.get(id);
|
|
1559
|
+
rows.push({
|
|
1560
|
+
addr: `${kind}:${id}`,
|
|
1561
|
+
kind,
|
|
1562
|
+
id,
|
|
1563
|
+
name,
|
|
1564
|
+
aliases,
|
|
1565
|
+
importance,
|
|
1566
|
+
role,
|
|
1567
|
+
states,
|
|
1568
|
+
description_present: descPresent === "yes",
|
|
1569
|
+
...(appearances
|
|
1570
|
+
? {
|
|
1571
|
+
scenes: seen?.scenes ?? 0,
|
|
1572
|
+
episodes_count: seen?.episodes.size ?? 0,
|
|
1573
|
+
episodes: seen ? compactEpisodeRanges(seen.episodes) : "",
|
|
1574
|
+
}
|
|
1575
|
+
: {}),
|
|
1576
|
+
});
|
|
992
1577
|
}
|
|
993
|
-
return
|
|
1578
|
+
return {
|
|
1579
|
+
lines,
|
|
1580
|
+
rows,
|
|
1581
|
+
rowColumns: [
|
|
1582
|
+
"addr",
|
|
1583
|
+
"kind",
|
|
1584
|
+
"id",
|
|
1585
|
+
"name",
|
|
1586
|
+
"aliases",
|
|
1587
|
+
"importance",
|
|
1588
|
+
"role",
|
|
1589
|
+
"states",
|
|
1590
|
+
"description_present",
|
|
1591
|
+
...(appearances ? ["scenes", "episodes_count", "episodes"] : []),
|
|
1592
|
+
],
|
|
1593
|
+
};
|
|
994
1594
|
}
|
|
995
1595
|
export function splitSpeakerList(raw) {
|
|
996
1596
|
if (Array.isArray(raw))
|
|
@@ -999,22 +1599,21 @@ export function splitSpeakerList(raw) {
|
|
|
999
1599
|
}
|
|
1000
1600
|
export function blankCreateReport(session, validation, idempotent) {
|
|
1001
1601
|
const passed = Boolean(validation["passed"]);
|
|
1002
|
-
const resultLines = [
|
|
1602
|
+
const resultLines = [`created: blank script (Script v${session.version})`, `validation: ${passed ? "passed" : "needs repair"}`];
|
|
1003
1603
|
if (session.remote) {
|
|
1004
1604
|
resultLines.push(`revision: ${session.revision}`);
|
|
1005
1605
|
if (idempotent !== null)
|
|
1006
1606
|
resultLines.push(`idempotent: ${String(idempotent).toLowerCase()}`);
|
|
1007
1607
|
}
|
|
1008
1608
|
return {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
],
|
|
1609
|
+
text: resultLines.join("\n"),
|
|
1610
|
+
data: {
|
|
1611
|
+
op: "script.create",
|
|
1612
|
+
changed: true,
|
|
1613
|
+
validation,
|
|
1614
|
+
script_path: session.artifactLabel,
|
|
1615
|
+
...(session.remote ? { revision: session.revision, idempotent } : {}),
|
|
1616
|
+
},
|
|
1018
1617
|
};
|
|
1019
1618
|
}
|
|
1020
1619
|
// ----- add-* (register new entities) ----------------------------------------
|
|
@@ -1023,12 +1622,11 @@ export function blankCreateReport(session, validation, idempotent) {
|
|
|
1023
1622
|
export function requireNameArg(opts, verb, example) {
|
|
1024
1623
|
const args = asList(opts["_args"]);
|
|
1025
1624
|
if (args.length < 1) {
|
|
1026
|
-
throw new CliError(
|
|
1625
|
+
throw new CliError("Name missing.", {
|
|
1027
1626
|
exitCode: EXIT_USAGE,
|
|
1028
|
-
required: ["<name>"],
|
|
1029
|
-
received: ["<empty>"],
|
|
1030
|
-
nextSteps: [example],
|
|
1031
1627
|
errorCode: "ARGS_MISSING",
|
|
1628
|
+
hint: example,
|
|
1629
|
+
data: { expected: ["<name>"], actual: ["<empty>"] },
|
|
1032
1630
|
});
|
|
1033
1631
|
}
|
|
1034
1632
|
return args[0];
|