@a-company/atelier 0.37.0 → 0.38.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/dist/{chunk-JPZ4F4PW.js → chunk-3ARBOSWY.js} +64 -5
- package/dist/chunk-3ARBOSWY.js.map +1 -0
- package/dist/cli.js +11469 -413
- package/dist/cli.js.map +1 -1
- package/dist/{dist-M67UZGFQ.js → dist-3YQK6PI6.js} +2 -2
- package/dist/index.cjs +3193 -227
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +701 -8
- package/dist/index.d.ts +701 -8
- package/dist/index.js +7237 -72
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +2898 -507
- package/dist/mcp.js.map +1 -1
- package/package.json +6 -6
- package/src/web/inline-app.ts +55 -4
- package/src/web/timeline-state-types.ts +28 -0
- package/src/web/timeline-view.test.ts +99 -0
- package/src/web/timeline-view.ts +339 -0
- package/src/web/workspace-app.ts +3146 -0
- package/templates/workspace/.claude/agents/atelier-iris.md +75 -0
- package/templates/workspace/.claude/agents/atelier-lux.md +67 -0
- package/templates/workspace/.claude/agents/atelier-quill.md +61 -0
- package/templates/workspace/.gitignore +30 -0
- package/templates/workspace/.paradigm/personas/_shared/cascade-merge.md +172 -0
- package/templates/workspace/CLAUDE.md +93 -0
- package/templates/workspace/README.md +75 -0
- package/templates/workspace/SETUP.md +127 -0
- package/templates/workspace/_brand/.atelier-brand.yaml +34 -0
- package/templates/workspace/_brand/DESIGN.md +56 -0
- package/templates/workspace/_brand/SCRIPT.md +41 -0
- package/templates/workspace/_brand/STORYBOARD.md +33 -0
- package/templates/workspace/_packs/README.md +54 -0
- package/templates/workspace/projects/README.md +49 -0
- package/templates/workspace/workspace.atelier +22 -0
- package/university/index.yaml +1 -1
- package/dist/chunk-5QQESXI6.js +0 -4432
- package/dist/chunk-5QQESXI6.js.map +0 -1
- package/dist/chunk-JPZ4F4PW.js.map +0 -1
- package/dist/cli.cjs +0 -6313
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/mcp.cjs +0 -5462
- package/dist/mcp.cjs.map +0 -1
- /package/dist/{dist-M67UZGFQ.js.map → dist-3YQK6PI6.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { AtelierDocument, StudioRecipe, VideoProjectManifest, CutEntry, VideoCutList, VideoTranscript, Layer, Delta, TranscriptWord } from '@a-company/atelier-types';
|
|
2
|
+
import { AtelierDocument, StudioRecipe, VideoProjectManifest, CutEntry, VideoCutList, VideoTranscript, Layer, Delta, TranscriptWord, SlideTransition, ProjectKind } from '@a-company/atelier-types';
|
|
3
3
|
import { ResolvedFrame } from '@a-company/atelier-core';
|
|
4
|
+
import { DesignArtifact, ScriptArtifact, StoryboardArtifact } from '@a-company/atelier-schema';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Validate an .atelier file: parse YAML, check schema, check delta overlaps.
|
|
@@ -261,17 +262,24 @@ declare function carouselFileName(index: number, total: number, imagePath: strin
|
|
|
261
262
|
*/
|
|
262
263
|
declare function carouselCommand(program: Command): void;
|
|
263
264
|
|
|
264
|
-
/**
|
|
265
|
+
/**
|
|
266
|
+
* Asset summary entry. v1.0: `usedByStates` removed — audio is no longer
|
|
267
|
+
* state-scoped (it's an AudioVisual layer), so layer-only tracking is now
|
|
268
|
+
* the complete picture.
|
|
269
|
+
*/
|
|
265
270
|
interface AssetInfo {
|
|
266
271
|
assetId: string;
|
|
267
272
|
type: string;
|
|
268
273
|
src: string;
|
|
269
274
|
description?: string;
|
|
270
275
|
usedByLayers: string[];
|
|
271
|
-
usedByStates: string[];
|
|
272
276
|
}
|
|
273
277
|
/**
|
|
274
278
|
* Extract asset info from a parsed AtelierDocument.
|
|
279
|
+
*
|
|
280
|
+
* Walks all layers (image, video, audio) for assetId references. Pre-v1
|
|
281
|
+
* docs that still carry `state.audio` are silently migrated by the parser
|
|
282
|
+
* (audio field stripped); no per-state asset usage is tracked.
|
|
275
283
|
*/
|
|
276
284
|
declare function getAssets(doc: AtelierDocument): AssetInfo[];
|
|
277
285
|
/**
|
|
@@ -310,6 +318,19 @@ interface RenderOptions {
|
|
|
310
318
|
format: RenderFormat;
|
|
311
319
|
states?: string[];
|
|
312
320
|
onProgress?: (info: ProgressInfo) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Absolute path of the .atelier document being rendered. Used to resolve
|
|
323
|
+
* relative `media/...` src paths in VideoVisual layers. When set, the
|
|
324
|
+
* pipeline detects the first video layer, passes the source MOV/MP4 to
|
|
325
|
+
* ffmpeg as a SECOND input, and uses an overlay filter to composite the
|
|
326
|
+
* caption canvas ON TOP of the source video frames (instead of feeding
|
|
327
|
+
* the encoder a synthetic canvas-only stream). Source audio is also
|
|
328
|
+
* mapped through verbatim. This is what closes the
|
|
329
|
+
* RENDER-AUDIO-ISSUE.md gap reported by the agent team on 2026-05-28.
|
|
330
|
+
*
|
|
331
|
+
* Omitted → legacy behaviour (canvas-only frames, no audio).
|
|
332
|
+
*/
|
|
333
|
+
docPath?: string;
|
|
313
334
|
}
|
|
314
335
|
interface ProgressInfo {
|
|
315
336
|
frame: number;
|
|
@@ -326,8 +347,26 @@ interface RenderResult {
|
|
|
326
347
|
}
|
|
327
348
|
/** Check whether FFmpeg is available on the system PATH. */
|
|
328
349
|
declare function checkFfmpeg(): Promise<boolean>;
|
|
329
|
-
/**
|
|
330
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Build the FFmpeg argument array for the given format. Pure function.
|
|
352
|
+
*
|
|
353
|
+
* When `sourceVideoPath` is provided, ffmpeg gets TWO inputs:
|
|
354
|
+
* - Input 0: the source video file (decoded photographic frames + audio)
|
|
355
|
+
* - Input 1: raw BGRA canvas frames on stdin (the caption overlay)
|
|
356
|
+
*
|
|
357
|
+
* A `[0:v][1:v]overlay` filter composites the canvas on top of the source
|
|
358
|
+
* video at matching timestamps. Source audio is copied through with
|
|
359
|
+
* `-map 0:a -c:a copy` (no re-encode). The MP4 output then has full source
|
|
360
|
+
* video bitrate + source audio + caption overlays — what the studio's
|
|
361
|
+
* MediaRecorder path produces by accident, but deterministically and
|
|
362
|
+
* scriptable for agent use.
|
|
363
|
+
*
|
|
364
|
+
* When `sourceVideoPath` is omitted, falls back to the legacy stdin-only
|
|
365
|
+
* mode (canvas frames only, no source pixels, no audio). Used by docs
|
|
366
|
+
* without any VideoVisual layers (e.g. text-only animations, image
|
|
367
|
+
* carousels).
|
|
368
|
+
*/
|
|
369
|
+
declare function buildFfmpegArgs(width: number, height: number, fps: number, format: RenderFormat, output: string, sourceVideoPath?: string): string[];
|
|
331
370
|
/**
|
|
332
371
|
* Render an Atelier document to a video/GIF file via FFmpeg.
|
|
333
372
|
* Dynamically imports `canvas` (node-canvas) — fails with a helpful
|
|
@@ -460,8 +499,20 @@ declare function trimProject(projectDir: string, options?: TrimOptions): Promise
|
|
|
460
499
|
/** Register `atelier trim` on the Commander program */
|
|
461
500
|
declare function trimCommand(program: Command): void;
|
|
462
501
|
|
|
463
|
-
/**
|
|
464
|
-
|
|
502
|
+
/**
|
|
503
|
+
* Whisper model selection (size/quality tradeoff).
|
|
504
|
+
*
|
|
505
|
+
* The set mirrors whisper.cpp's published model registry on HuggingFace
|
|
506
|
+
* (https://huggingface.co/ggerganov/whisper.cpp). Quantized variants and
|
|
507
|
+
* the v3-turbo line are first-class — creators get the best local-quality
|
|
508
|
+
* tradeoff without needing to know the model-zoo URL by heart.
|
|
509
|
+
*
|
|
510
|
+
* Note: the type is intentionally open to `string` via the fallback union so a
|
|
511
|
+
* creator can pass a future model name (e.g. a new quant variant) without us
|
|
512
|
+
* having to ship a code change. `resolveWhisperModelPath` will attempt to
|
|
513
|
+
* resolve any string against the cache + HuggingFace registry.
|
|
514
|
+
*/
|
|
515
|
+
type WhisperModel = "tiny" | "tiny.en" | "base" | "base.en" | "small" | "small.en" | "medium" | "medium.en" | "large-v3" | "large-v3-turbo" | "large-v3-turbo-q5_0" | "large-v3-q5_0" | (string & {});
|
|
465
516
|
interface WhisperOptions {
|
|
466
517
|
/** Model size (default: "base.en") */
|
|
467
518
|
model?: WhisperModel;
|
|
@@ -469,6 +520,11 @@ interface WhisperOptions {
|
|
|
469
520
|
language?: string;
|
|
470
521
|
/** Explicit path to the model file — overrides model lookup by name */
|
|
471
522
|
modelPath?: string;
|
|
523
|
+
/**
|
|
524
|
+
* Optional progress callback for model download (bytes received, total bytes
|
|
525
|
+
* when known). Surfaced as a one-time download on first use of any model name.
|
|
526
|
+
*/
|
|
527
|
+
onProgress?: (received: number, total: number | null) => void;
|
|
472
528
|
}
|
|
473
529
|
/** Backend that produced a transcript run */
|
|
474
530
|
type WhisperBackend = "whisper-cpp" | "openai-api" | "none";
|
|
@@ -486,6 +542,11 @@ declare function probeWhisper(): Promise<WhisperBackend>;
|
|
|
486
542
|
* Note: whisper-cli writes JSON to stdout when given --output-json and "-"
|
|
487
543
|
* as the output destination. Some builds always write to a file with the
|
|
488
544
|
* `.json` suffix appended to the input path; we handle both.
|
|
545
|
+
*
|
|
546
|
+
* Model resolution: if `options.modelPath` is set, it's passed through. Else
|
|
547
|
+
* `options.model` (default `base.en`) is resolved via `resolveWhisperModelPath`,
|
|
548
|
+
* downloading from the HuggingFace public registry on first use. Once the
|
|
549
|
+
* model is cached, no network call is made.
|
|
489
550
|
*/
|
|
490
551
|
declare function runWhisperCpp(sourcePath: string, options?: WhisperOptions): Promise<string>;
|
|
491
552
|
/**
|
|
@@ -564,6 +625,15 @@ declare function rewriteCaptionLayers(doc: AtelierDocument, transcript: VideoTra
|
|
|
564
625
|
interface TranscribeOptions {
|
|
565
626
|
/** Whisper model selection */
|
|
566
627
|
model?: WhisperModel;
|
|
628
|
+
/** Explicit path to the model file — overrides model short-name lookup */
|
|
629
|
+
modelPath?: string;
|
|
630
|
+
/**
|
|
631
|
+
* Override the source video path. By default `transcribeProject` uses the
|
|
632
|
+
* legacy VideoProject scanner (`<projectDir>/source.<ext>`). Passing
|
|
633
|
+
* `sourcePath` lets the multi-media ingest target an arbitrary media file
|
|
634
|
+
* (e.g. `<projectDir>/media/<basename>`) without copying it to root.
|
|
635
|
+
*/
|
|
636
|
+
sourcePath?: string;
|
|
567
637
|
/** BCP-47 language hint (omit for autodetect) */
|
|
568
638
|
language?: string;
|
|
569
639
|
/** Discard existing user edits; full fresh transcript */
|
|
@@ -683,6 +753,255 @@ declare function recipeCommand(program: Command): void;
|
|
|
683
753
|
*/
|
|
684
754
|
declare function applyRecipeCommand(program: Command): void;
|
|
685
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Atelier creator learning-mode I/O.
|
|
758
|
+
*
|
|
759
|
+
* `learning-mode.yaml` lives at `<project-dir>/.atelier/learning-mode.yaml`
|
|
760
|
+
* and stores the creator's pre-declared autonomy posture (TD-2026-05-26-646):
|
|
761
|
+
*
|
|
762
|
+
* - `ambient` — the agent team captures corrections silently and surfaces
|
|
763
|
+
* patterns when confident.
|
|
764
|
+
* - `explicit` — nothing locks in without the creator's "yes"; high control,
|
|
765
|
+
* more friction.
|
|
766
|
+
*
|
|
767
|
+
* Set once by `atelier init`. The system never auto-prompts to switch — the
|
|
768
|
+
* creator changes mode by editing the file. Iris (and friends) read it at
|
|
769
|
+
* session start to calibrate their behavior.
|
|
770
|
+
*
|
|
771
|
+
* Format is hand-rolled YAML (a handful of scalars) so we don't pull in a YAML
|
|
772
|
+
* dep here; the schema is intentionally tiny and stable.
|
|
773
|
+
*/
|
|
774
|
+
type LearningMode = "ambient" | "explicit";
|
|
775
|
+
declare const LEARNING_MODES: readonly LearningMode[];
|
|
776
|
+
declare const LEARNING_MODE_VERSION: "1.0";
|
|
777
|
+
/** Shape persisted to disk. */
|
|
778
|
+
interface LearningModeFile {
|
|
779
|
+
/** Schema version of this file. */
|
|
780
|
+
version: string;
|
|
781
|
+
/** Creator's chosen autonomy posture. */
|
|
782
|
+
mode: LearningMode;
|
|
783
|
+
/** ISO 8601 timestamp set by the writer. */
|
|
784
|
+
chosen_at: string;
|
|
785
|
+
}
|
|
786
|
+
/** Absolute path to the learning-mode file for a project. */
|
|
787
|
+
declare function learningModePath(projectDir: string): string;
|
|
788
|
+
/**
|
|
789
|
+
* Write `<projectDir>/.atelier/learning-mode.yaml` with the given mode and a
|
|
790
|
+
* fresh ISO timestamp. Creates `<projectDir>/.atelier/` if absent. Overwrites
|
|
791
|
+
* any existing file — re-running init is supported and refreshes the
|
|
792
|
+
* timestamp (mode is whatever the caller passes; the caller decides whether
|
|
793
|
+
* to re-prompt). Returns the absolute path written.
|
|
794
|
+
*/
|
|
795
|
+
declare function writeLearningMode(projectDir: string, mode: LearningMode, opts?: {
|
|
796
|
+
chosenAt?: Date;
|
|
797
|
+
}): string;
|
|
798
|
+
/**
|
|
799
|
+
* Read `<projectDir>/.atelier/learning-mode.yaml` if present. Returns `undefined`
|
|
800
|
+
* when the file does not exist. Throws a clear Error when the file exists but
|
|
801
|
+
* is malformed (missing required fields, unknown mode). Hand-rolled scalar
|
|
802
|
+
* parser — no YAML dep — matched to whatever `writeLearningMode` emits.
|
|
803
|
+
*/
|
|
804
|
+
declare function readLearningMode(projectDir: string): LearningModeFile | undefined;
|
|
805
|
+
/**
|
|
806
|
+
* Render the canonical YAML body — a fixed-shape doc with header comments
|
|
807
|
+
* pointing the creator at where to change the mode (TD-2026-05-26-646). Kept
|
|
808
|
+
* here so the writer and any future reader/round-tripper agree on the shape.
|
|
809
|
+
*/
|
|
810
|
+
declare function renderLearningModeYaml(file: LearningModeFile): string;
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* `atelier init <project-dir>` — first-run setup for a creator Project.
|
|
814
|
+
*
|
|
815
|
+
* Sequence (Phase 3, delegation C; see
|
|
816
|
+
* docs/keynote/paradigm/phase-3-reconciliation-notes.md §3):
|
|
817
|
+
*
|
|
818
|
+
* 1. Create `<project-dir>` if absent (recursive). Refuse if the path
|
|
819
|
+
* exists as a file rather than a directory. Existing non-empty dir is
|
|
820
|
+
* a "complete the init" workflow — proceed with a note in output.
|
|
821
|
+
* 2. Determine learning mode. `--mode <ambient|explicit>` skips the
|
|
822
|
+
* prompt; otherwise readline-prompts the creator (single question, two
|
|
823
|
+
* options, max 3 attempts). Bad `--mode` value exits 1 before any fs
|
|
824
|
+
* writes (TD-2026-05-26-646: autonomy posture is pre-declared).
|
|
825
|
+
* 3. Write `<project-dir>/.atelier/learning-mode.yaml` with the choice
|
|
826
|
+
* and an ISO timestamp (delegated to #learning-mode-io).
|
|
827
|
+
* 4. Create `<project-dir>/.atelier/mind-map/` with a README explaining
|
|
828
|
+
* what to put there. README is preserved on re-init (TD-2026-05-26-271:
|
|
829
|
+
* nothing in the mind-map leaves the machine).
|
|
830
|
+
* 5. Unless `--no-scaffold`, drop DESIGN/SCRIPT/STORYBOARD via the
|
|
831
|
+
* existing #cli-artifacts `scaffoldArtifacts` helper. Re-init swallows
|
|
832
|
+
* the refuse-to-overwrite throw as a soft skip (the operation is
|
|
833
|
+
* explicitly idempotent — see init-idempotent-re-run-safe aspect).
|
|
834
|
+
* 6. Unless `--no-git`, run `git init` in `<project-dir>` if it isn't
|
|
835
|
+
* already inside a git repo. Writes a minimal `.gitignore` only when
|
|
836
|
+
* one doesn't exist.
|
|
837
|
+
*
|
|
838
|
+
* Output: human-formatted step-by-step summary by default; `--json` emits
|
|
839
|
+
* the documented machine-readable shape. Exit 0 on success, 1 on any
|
|
840
|
+
* irrecoverable error (file-not-dir, can't determine mode, fs failure).
|
|
841
|
+
*
|
|
842
|
+
* The Commander wiring is a thin shell around the pure `runInit(opts)`
|
|
843
|
+
* helper so test code drives the same code path without process.exit
|
|
844
|
+
* shenanigans (matches the artifacts.ts split).
|
|
845
|
+
*/
|
|
846
|
+
declare function initCommand(program: Command): void;
|
|
847
|
+
/** Input to `runInit`. Booleans are positive-form (caller pre-resolves --no-X). */
|
|
848
|
+
interface RunInitOptions {
|
|
849
|
+
projectDir: string;
|
|
850
|
+
/** Pre-supplied mode (skips the prompt). */
|
|
851
|
+
mode?: string;
|
|
852
|
+
/**
|
|
853
|
+
* When the mode was inherited from a workspace (not explicitly chosen
|
|
854
|
+
* by the creator for this project), the per-project learning-mode.yaml
|
|
855
|
+
* gets a comment noting the inheritance. The wrapper sets this when
|
|
856
|
+
* `--mode` was absent and a workspace's `default_mode` was used.
|
|
857
|
+
*/
|
|
858
|
+
modeInheritedFromWorkspace?: boolean;
|
|
859
|
+
/** Run `git init` when not already in a repo. Default true. */
|
|
860
|
+
git: boolean;
|
|
861
|
+
/** Drop DESIGN/SCRIPT/STORYBOARD via `scaffoldArtifacts`. Default true. */
|
|
862
|
+
scaffold: boolean;
|
|
863
|
+
/** Forward to `scaffoldArtifacts({ force })`. Default false. */
|
|
864
|
+
force: boolean;
|
|
865
|
+
/** Suppress interactive prompt fallback (used by --json path). Default false. */
|
|
866
|
+
json: boolean;
|
|
867
|
+
/**
|
|
868
|
+
* Drop a minimal `project.atelier` manifest at the project root so this
|
|
869
|
+
* directory is recognized as a Project by legacy discovery callers. The
|
|
870
|
+
* CLI surface (`atelier init`) leaves this true so existing tooling that
|
|
871
|
+
* keys on the manifest's presence keeps working; the workspace `+ new`
|
|
872
|
+
* surface (via `createProjectInWorkspace`) passes `false` so the project
|
|
873
|
+
* stays "lazy" until its first composed `.atelier` doc lands. Default
|
|
874
|
+
* true (preserves pre-pivot behavior on the CLI path).
|
|
875
|
+
*/
|
|
876
|
+
writeManifest?: boolean;
|
|
877
|
+
/**
|
|
878
|
+
* Test/programmatic override: a function that resolves to the chosen mode
|
|
879
|
+
* when no `--mode` was passed. Production wiring uses the readline prompt.
|
|
880
|
+
* Returning `undefined` or throwing aborts with the standard "couldn't
|
|
881
|
+
* determine learning mode" error.
|
|
882
|
+
*/
|
|
883
|
+
promptForMode?: () => Promise<LearningMode>;
|
|
884
|
+
}
|
|
885
|
+
/** Result returned by `runInit` and serialized for `--json`. */
|
|
886
|
+
interface InitResult {
|
|
887
|
+
/** True on the success path. */
|
|
888
|
+
ok: true;
|
|
889
|
+
projectDir: string;
|
|
890
|
+
mode: LearningMode;
|
|
891
|
+
learningModeFile: string;
|
|
892
|
+
/** Absolute paths of artifacts written by the scaffold step (may be empty). */
|
|
893
|
+
scaffolded: string[];
|
|
894
|
+
/** Absolute paths of artifacts skipped (already existed, no --force). */
|
|
895
|
+
scaffoldSkipped: string[];
|
|
896
|
+
mindMapDir: string;
|
|
897
|
+
/** True when `git init` ran in this invocation. */
|
|
898
|
+
gitInitialized: boolean;
|
|
899
|
+
/** True when the dir was already inside an existing git repo. */
|
|
900
|
+
alreadyInRepo: boolean;
|
|
901
|
+
/** Notes accumulated during the run (re-init warnings, etc.). */
|
|
902
|
+
notes: string[];
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Pure helper for `atelier init`. Throws on any irrecoverable error; the
|
|
906
|
+
* Commander wrapper catches and maps to `process.exit(1)`. Re-runnable on an
|
|
907
|
+
* already-initialized dir (scaffold's refuse-to-overwrite is downgraded to a
|
|
908
|
+
* soft skip in this surface — see init-idempotent-re-run-safe aspect).
|
|
909
|
+
*/
|
|
910
|
+
declare function runInit(opts: RunInitOptions): Promise<InitResult>;
|
|
911
|
+
/**
|
|
912
|
+
* Returns true when `dir` (or any ancestor) sits inside an existing git work
|
|
913
|
+
* tree. Implemented via `git rev-parse --show-toplevel`; non-zero exit and
|
|
914
|
+
* any spawn failure (missing git, etc.) are treated as "not in a repo" so the
|
|
915
|
+
* caller falls through to `git init`.
|
|
916
|
+
*/
|
|
917
|
+
declare function isInsideGitRepo(dir: string): boolean;
|
|
918
|
+
/** Parse a single answer to the mode prompt. Returns undefined on no match. */
|
|
919
|
+
declare function parseModeAnswer(answer: string): LearningMode | undefined;
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* `atelier artifacts <verb>` — first user-facing surface over the
|
|
923
|
+
* Phase 2 front-of-pipeline artifact schemas (DESIGN/SCRIPT/STORYBOARD).
|
|
924
|
+
*
|
|
925
|
+
* Phase 2 lands the schemas + the `^valid-artifact-set` gate; this CLI
|
|
926
|
+
* verb is the on-disk runner. `atelier artifacts validate <project-dir>`
|
|
927
|
+
* walks a Project directory for the three artifact files, parses each
|
|
928
|
+
* one that's present, runs the cross-artifact validator on whatever
|
|
929
|
+
* parsed, and prints a human or JSON report.
|
|
930
|
+
*
|
|
931
|
+
* Exit-code matrix:
|
|
932
|
+
* - parse error on any file → 1
|
|
933
|
+
* - validator returns ok: false → 1
|
|
934
|
+
* - warnings only, no errors → 0 (per spec §9: pre-VO duration overrun
|
|
935
|
+
* is warn-only and must NOT fail the gate)
|
|
936
|
+
* - missing artifacts → 0 (per validator: only what's present is checked)
|
|
937
|
+
*
|
|
938
|
+
* Output style mirrors `recipe.ts` — `console.log` for results, status
|
|
939
|
+
* lines prefixed with PASS/FAIL/WARN, `--json` toggles a machine
|
|
940
|
+
* readable payload. (CLI stdout IS the deliverable here; the generic
|
|
941
|
+
* Paradigm-logger rule does not apply to CLI verbs — see `recipe.ts`,
|
|
942
|
+
* `validate.ts`, etc., which all follow this same convention.)
|
|
943
|
+
*/
|
|
944
|
+
declare function artifactsCommand(program: Command): void;
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Pure filesystem walker for the front-of-pipeline artifact triplet
|
|
948
|
+
* (DESIGN.md, SCRIPT.md, STORYBOARD.md). Sibling-of-`project.atelier`
|
|
949
|
+
* convention per `docs/keynote/paradigm/atelier-front-of-pipeline-artifacts.md`
|
|
950
|
+
* §2 — the three files sit at the root of a Project directory.
|
|
951
|
+
*
|
|
952
|
+
* This module is intentionally pure: no stdout/exit-code coupling. The
|
|
953
|
+
* `atelier artifacts validate` CLI command (and any future agent / MCP
|
|
954
|
+
* caller) consumes the structured result and decides what to print.
|
|
955
|
+
*
|
|
956
|
+
* Any subset of the three files may exist — the absent ones are
|
|
957
|
+
* surfaced via `missing` (informational, NOT an error). The validator
|
|
958
|
+
* (`validateArtifactSet`) runs on whatever was successfully parsed.
|
|
959
|
+
*/
|
|
960
|
+
/** Filenames the walker looks for at the project-dir root. */
|
|
961
|
+
declare const ARTIFACT_FILENAMES: {
|
|
962
|
+
readonly design: "DESIGN.md";
|
|
963
|
+
readonly script: "SCRIPT.md";
|
|
964
|
+
readonly storyboard: "STORYBOARD.md";
|
|
965
|
+
};
|
|
966
|
+
/** Per-file parse failure surface. */
|
|
967
|
+
interface ArtifactParseError {
|
|
968
|
+
/** Which artifact triplet slot failed. */
|
|
969
|
+
artifact: "design" | "script" | "storyboard";
|
|
970
|
+
/** Absolute path to the file that failed. */
|
|
971
|
+
file: string;
|
|
972
|
+
/** Human-readable failure message. */
|
|
973
|
+
message: string;
|
|
974
|
+
}
|
|
975
|
+
/** Structured result of loading the three artifacts from a project dir. */
|
|
976
|
+
interface LoadedArtifactProject {
|
|
977
|
+
/** Absolute project-dir path that was inspected. */
|
|
978
|
+
projectDir: string;
|
|
979
|
+
/** Successfully-parsed DESIGN.md, if present and valid. */
|
|
980
|
+
design?: DesignArtifact;
|
|
981
|
+
/** Successfully-parsed SCRIPT.md, if present and valid. */
|
|
982
|
+
script?: ScriptArtifact;
|
|
983
|
+
/** Successfully-parsed STORYBOARD.md, if present and valid. */
|
|
984
|
+
storyboard?: StoryboardArtifact;
|
|
985
|
+
/**
|
|
986
|
+
* Names of artifact files that were not found on disk.
|
|
987
|
+
* NOT an error — absent artifacts are valid per the spec
|
|
988
|
+
* (only what is present is validated).
|
|
989
|
+
*/
|
|
990
|
+
missing: string[];
|
|
991
|
+
/** Per-file parse errors. Empty when all present artifacts parsed cleanly. */
|
|
992
|
+
parseErrors: ArtifactParseError[];
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Walk a project directory for DESIGN.md / SCRIPT.md / STORYBOARD.md and
|
|
996
|
+
* parse whichever are present.
|
|
997
|
+
*
|
|
998
|
+
* Throws (synchronously) only when `projectDir` itself does not exist or
|
|
999
|
+
* is not a directory — per-file parse failures are captured into the
|
|
1000
|
+
* returned `parseErrors` array so callers can show every problem at
|
|
1001
|
+
* once instead of one at a time.
|
|
1002
|
+
*/
|
|
1003
|
+
declare function loadArtifactsFromProject(projectDir: string): LoadedArtifactProject;
|
|
1004
|
+
|
|
686
1005
|
declare const RECIPE_VERSION = "1.0";
|
|
687
1006
|
interface LoadedRecipe {
|
|
688
1007
|
recipe: StudioRecipe;
|
|
@@ -741,4 +1060,378 @@ declare function renderRecipeWithDefaults(recipe: StudioRecipe): StudioRecipe;
|
|
|
741
1060
|
/** Serialize a recipe back to YAML for `recipe show` output */
|
|
742
1061
|
declare function recipeToYaml(recipe: StudioRecipe): string;
|
|
743
1062
|
|
|
744
|
-
|
|
1063
|
+
interface ComposeVideoProjectOptions {
|
|
1064
|
+
/** Absolute path to the project root (contains the doc at docPath). */
|
|
1065
|
+
projectDir: string;
|
|
1066
|
+
/**
|
|
1067
|
+
* Project-relative path to the .atelier composition document this helper
|
|
1068
|
+
* reads/writes. Required — no default. Caller computes this from the
|
|
1069
|
+
* ingest slug (e.g. `"img-7346.atelier"`, `"slides/slide-1.atelier"`).
|
|
1070
|
+
* The parent directory is created on demand. The helper NEVER touches
|
|
1071
|
+
* `project.atelier` specifically; only this caller-supplied path.
|
|
1072
|
+
*/
|
|
1073
|
+
docPath: string;
|
|
1074
|
+
/**
|
|
1075
|
+
* Absolute path to the source media file. Used to (a) compute a canonical
|
|
1076
|
+
* assetId/layer src reference, and (b) probe duration via ffprobe.
|
|
1077
|
+
*/
|
|
1078
|
+
sourceFile: string;
|
|
1079
|
+
/**
|
|
1080
|
+
* Whether to run whisper transcription. Default true. When false, only the
|
|
1081
|
+
* state-duration + video-layer wiring runs (useful when re-composing a
|
|
1082
|
+
* project that already has transcript.json).
|
|
1083
|
+
*/
|
|
1084
|
+
transcribe?: boolean;
|
|
1085
|
+
/** Forwarded to `transcribeProject` — model selection, language, etc. */
|
|
1086
|
+
transcribeOptions?: TranscribeOptions;
|
|
1087
|
+
/**
|
|
1088
|
+
* When set, after `transcribeProject` writes the root `transcript.json`
|
|
1089
|
+
* the result is mirrored to `<projectDir>/transcripts/<stem>.json` (per
|
|
1090
|
+
* #transcribe-orchestrator). Pass the basename of the file the creator
|
|
1091
|
+
* just ingested — e.g. `IMG_7347.MOV` for a drag-drop video, or
|
|
1092
|
+
* `source.mp4` for the legacy single-source layout. Omit for
|
|
1093
|
+
* single-source projects that only need the root `transcript.json`
|
|
1094
|
+
* (preserves the legacy `atelier transcribe` contract).
|
|
1095
|
+
*/
|
|
1096
|
+
mediaBasename?: string;
|
|
1097
|
+
/**
|
|
1098
|
+
* Test injection point — production wiring uses the real `transcribeProject`.
|
|
1099
|
+
* Mirrors the same hook on `runMediaIngest`. Used by both the single-source
|
|
1100
|
+
* `transcribeProject` path and the per-file `transcribeMediaFile` path
|
|
1101
|
+
* (the orchestrator forwards this fn through unchanged).
|
|
1102
|
+
*/
|
|
1103
|
+
transcribeFn?: (dir: string, opts: TranscribeOptions) => Promise<TranscribeResult>;
|
|
1104
|
+
/**
|
|
1105
|
+
* Test injection point — production wiring uses `probeDuration` (ffprobe).
|
|
1106
|
+
* When omitted and ffprobe fails (or isn't installed), we fall back to the
|
|
1107
|
+
* transcript's last word end time.
|
|
1108
|
+
*/
|
|
1109
|
+
probeDurationFn?: (sourcePath: string) => Promise<number>;
|
|
1110
|
+
/** Optional caption style overrides passed through to `buildCaptionLayers`. */
|
|
1111
|
+
captionOptions?: BuildCaptionsOptions;
|
|
1112
|
+
}
|
|
1113
|
+
interface ComposeVideoProjectResult {
|
|
1114
|
+
/** The project.atelier as written to disk. */
|
|
1115
|
+
doc: AtelierDocument;
|
|
1116
|
+
/** True iff a NEW clip layer was added (false on re-ingest of the same src). */
|
|
1117
|
+
addedVideoLayer: boolean;
|
|
1118
|
+
/** Slug derived from the source basename — `clip-<slug>` is the clip layer id. */
|
|
1119
|
+
clipSlug: string;
|
|
1120
|
+
/** Id of the clip layer just (re-)ingested. */
|
|
1121
|
+
clipLayerId: string;
|
|
1122
|
+
/** Name of the state whose duration was set/updated. */
|
|
1123
|
+
targetStateName: string;
|
|
1124
|
+
/** Total duration in seconds written into the target state. */
|
|
1125
|
+
targetDurationSeconds: number;
|
|
1126
|
+
/** Transcription result, when transcription ran. */
|
|
1127
|
+
transcribe?: TranscribeResult;
|
|
1128
|
+
/**
|
|
1129
|
+
* Absolute path to the per-file transcript mirror at
|
|
1130
|
+
* `<projectDir>/transcripts/<stem>.json`. Present only when caller passed
|
|
1131
|
+
* `mediaBasename` and transcription actually ran (and wasn't a dry-run).
|
|
1132
|
+
*/
|
|
1133
|
+
mirroredTranscriptPath?: string;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Compose a playable VideoProject from an ingested source + transcript.
|
|
1137
|
+
*
|
|
1138
|
+
* - Transcribes (unless `transcribe: false`) — writes the transcript file(s).
|
|
1139
|
+
* The legacy caption-rewrite side effect is neutralized below.
|
|
1140
|
+
* - Lazy-creates the `track-video-1` Group layer if absent.
|
|
1141
|
+
* - Appends a `clip-<slug>` VideoVisual layer parented to the track at the
|
|
1142
|
+
* end-of-track startFrame (or preserves placement on re-ingest of the same).
|
|
1143
|
+
* - Rebuilds per-clip captions as `cap-<slug>-N` parented to `clip-<slug>`,
|
|
1144
|
+
* leaving other clips' captions untouched.
|
|
1145
|
+
* - Regenerates the derived `timeline` summary block.
|
|
1146
|
+
* - Sets the target state's duration to cover all clips on all tracks.
|
|
1147
|
+
*/
|
|
1148
|
+
declare function composeVideoProject(opts: ComposeVideoProjectOptions): Promise<ComposeVideoProjectResult>;
|
|
1149
|
+
|
|
1150
|
+
interface ComposeImageProjectOptions {
|
|
1151
|
+
/** Absolute path to project root (contains or will contain the doc at docPath). */
|
|
1152
|
+
projectDir: string;
|
|
1153
|
+
/**
|
|
1154
|
+
* Project-relative path to the .atelier composition document this helper
|
|
1155
|
+
* reads/writes. Required — no default. Caller computes this from the
|
|
1156
|
+
* ingest slug (e.g. `"hero-image.atelier"`, `"slides/slide-1.atelier"`).
|
|
1157
|
+
* The parent directory is created on demand. The helper NEVER touches
|
|
1158
|
+
* `project.atelier` specifically; only this caller-supplied path.
|
|
1159
|
+
*/
|
|
1160
|
+
docPath: string;
|
|
1161
|
+
/** Absolute path to the source image file. */
|
|
1162
|
+
sourceFile: string;
|
|
1163
|
+
/** Override target basename inside media/. Defaults to basename(sourceFile). */
|
|
1164
|
+
mediaBasename?: string;
|
|
1165
|
+
}
|
|
1166
|
+
interface ComposeImageProjectResult {
|
|
1167
|
+
doc: AtelierDocument;
|
|
1168
|
+
/** True iff a new ImageVisual layer was added (vs. doc already had one). */
|
|
1169
|
+
addedImageLayer: boolean;
|
|
1170
|
+
/** Slug-based layer id, e.g. "img-photo-1". One-to-one with assetId. */
|
|
1171
|
+
layerId: string;
|
|
1172
|
+
/** Same as layerId — the asset key. */
|
|
1173
|
+
assetId: string;
|
|
1174
|
+
}
|
|
1175
|
+
/**
|
|
1176
|
+
* Compose (or re-compose) an Atelier image project from a single source image.
|
|
1177
|
+
*
|
|
1178
|
+
* - Single-image invariant: a second, differently-named image throws.
|
|
1179
|
+
* - Idempotent on re-ingest of the same basename.
|
|
1180
|
+
* - No timeline, no transcript — image projects render as a single frame.
|
|
1181
|
+
*/
|
|
1182
|
+
declare function composeImageProject(opts: ComposeImageProjectOptions): Promise<ComposeImageProjectResult>;
|
|
1183
|
+
|
|
1184
|
+
interface ComposeCarouselProjectOptions {
|
|
1185
|
+
/** Absolute path to project root (contains or will contain the doc at docPath). */
|
|
1186
|
+
projectDir: string;
|
|
1187
|
+
/**
|
|
1188
|
+
* Project-relative path to the CAROUSEL .atelier composition document this
|
|
1189
|
+
* helper reads/writes. Required — no default. Distinct from `slidePath`,
|
|
1190
|
+
* which is the slide being added. Caller computes this from the ingest
|
|
1191
|
+
* slug (e.g. `"gallery.atelier"`). The parent directory is created on
|
|
1192
|
+
* demand. The helper NEVER touches `project.atelier` specifically; only
|
|
1193
|
+
* this caller-supplied path.
|
|
1194
|
+
*/
|
|
1195
|
+
docPath: string;
|
|
1196
|
+
/** Absolute path to any .atelier file in the workspace — the slide to add. */
|
|
1197
|
+
slidePath: string;
|
|
1198
|
+
/** Workspace root used for cycle detection. Defaults to dirname(projectDir). */
|
|
1199
|
+
workspaceRoot?: string;
|
|
1200
|
+
/** Index to insert at; default = append. */
|
|
1201
|
+
insertAt?: number;
|
|
1202
|
+
transition?: SlideTransition;
|
|
1203
|
+
/** For image slides — frames to hold; ignored for video slides. */
|
|
1204
|
+
duration?: number;
|
|
1205
|
+
label?: string;
|
|
1206
|
+
}
|
|
1207
|
+
interface ComposeCarouselProjectResult {
|
|
1208
|
+
doc: AtelierDocument;
|
|
1209
|
+
slideIndex: number;
|
|
1210
|
+
addedSlide: boolean;
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Add a slide reference to a carousel project, cycle-safe.
|
|
1214
|
+
*
|
|
1215
|
+
* - Scaffolds a fresh kind:"carousel" doc if project.atelier is absent.
|
|
1216
|
+
* - Refuses to convert a non-carousel project.
|
|
1217
|
+
* - Refuses to introduce a cycle.
|
|
1218
|
+
* - Idempotent on duplicate slide src.
|
|
1219
|
+
*/
|
|
1220
|
+
declare function composeCarouselProject(opts: ComposeCarouselProjectOptions): Promise<ComposeCarouselProjectResult>;
|
|
1221
|
+
|
|
1222
|
+
/** Classification result for a media filename. */
|
|
1223
|
+
type IngestKind = "video" | "image" | "audio" | "unsupported";
|
|
1224
|
+
/**
|
|
1225
|
+
* Routing decision for a no-target ingest (file lands in the media bin).
|
|
1226
|
+
*
|
|
1227
|
+
* Post-pivot (TD-2026-05-28): either the file is a supported media kind and
|
|
1228
|
+
* goes to `add-to-bin`, or it's unsupported and we reject. Project kind no
|
|
1229
|
+
* longer factors in — composition is a separate, explicit step.
|
|
1230
|
+
*/
|
|
1231
|
+
interface BinIngestRoute {
|
|
1232
|
+
kind: IngestKind;
|
|
1233
|
+
target: "add-to-bin" | "reject";
|
|
1234
|
+
/** When target === "reject", the human-readable reason. */
|
|
1235
|
+
error?: string;
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Routing decision for an explicit-target ingest (canvas-area drop).
|
|
1239
|
+
*
|
|
1240
|
+
* The target-set path still uses the legacy four-way target enum since it
|
|
1241
|
+
* really does dispatch to compose-video / add-audio-clip / etc.
|
|
1242
|
+
*/
|
|
1243
|
+
interface IngestRoute {
|
|
1244
|
+
kind: IngestKind;
|
|
1245
|
+
target: "compose-video" | "compose-image" | "add-audio-clip" | "reject";
|
|
1246
|
+
/** When target === "reject", the human-readable reason. */
|
|
1247
|
+
error?: string;
|
|
1248
|
+
/**
|
|
1249
|
+
* When set, the project-relative `.atelier` doc path the route resolved
|
|
1250
|
+
* to. Populated by `routeIngestWithTarget` for explicit canvas drops so the
|
|
1251
|
+
* server-side handler can pass it straight to the composer / audio-add path
|
|
1252
|
+
* without re-deriving.
|
|
1253
|
+
*/
|
|
1254
|
+
docPath?: string;
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Classify a media filename by its extension. Case-insensitive — `.MOV` and
|
|
1258
|
+
* `.mov` both yield `"video"`. Returns `"unsupported"` for anything outside
|
|
1259
|
+
* the known video/image/audio extension lists, including files with no
|
|
1260
|
+
* extension at all.
|
|
1261
|
+
*/
|
|
1262
|
+
declare function classifyMediaFile(filename: string): IngestKind;
|
|
1263
|
+
/**
|
|
1264
|
+
* Route a no-target ingest into the project's media bin.
|
|
1265
|
+
*
|
|
1266
|
+
* Post-pivot (TD-2026-05-28) the no-target path doesn't care about project
|
|
1267
|
+
* kind — supported media always goes to `add-to-bin`, unsupported types are
|
|
1268
|
+
* rejected. Composition is a separate explicit step downstream.
|
|
1269
|
+
*
|
|
1270
|
+
* Mirrors `classifyMediaFile` for the kind detection; the routing decision is
|
|
1271
|
+
* a pure function of the filename.
|
|
1272
|
+
*/
|
|
1273
|
+
declare function routeIngest(filename: string): BinIngestRoute;
|
|
1274
|
+
/**
|
|
1275
|
+
* Routing matrix for explicit "add to this specific doc" drops (canvas-area
|
|
1276
|
+
* drag-drop, post-pivot). When the browser knows which doc is loaded in the
|
|
1277
|
+
* canvas, it sends `targetDocPath` + the target doc's `kind`; the routing
|
|
1278
|
+
* decision is then governed by the TARGET doc's kind — not the project's
|
|
1279
|
+
* inferred kind — because the user picked a specific doc.
|
|
1280
|
+
*
|
|
1281
|
+
* file kind target doc kind → target
|
|
1282
|
+
* ─────────────────────────────────────
|
|
1283
|
+
* video video → compose-video (docPath = targetDocPath)
|
|
1284
|
+
* audio video → add-audio-clip (docPath = targetDocPath)
|
|
1285
|
+
* image * → reject (v1)
|
|
1286
|
+
* video image / carousel → reject
|
|
1287
|
+
* audio image / carousel → reject
|
|
1288
|
+
* unsupported * → reject
|
|
1289
|
+
*
|
|
1290
|
+
* Pure / synchronous. The caller is responsible for verifying the target doc
|
|
1291
|
+
* exists on disk and reading its kind — this function only encodes the
|
|
1292
|
+
* compatibility matrix and produces the route decision.
|
|
1293
|
+
*/
|
|
1294
|
+
declare function routeIngestWithTarget(filename: string, targetDocPath: string, targetDocKind: ProjectKind | undefined): IngestRoute;
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* Project kind that a NEW doc can be created as.
|
|
1298
|
+
*
|
|
1299
|
+
* Owner explicitly excluded "blank" from v1 — only the three named kinds.
|
|
1300
|
+
* Future v1.1 may add `undefined` for blank docs.
|
|
1301
|
+
*/
|
|
1302
|
+
type DocKind = "video" | "image" | "carousel";
|
|
1303
|
+
interface CreateDocOptions {
|
|
1304
|
+
projectDir: string;
|
|
1305
|
+
name: string;
|
|
1306
|
+
kind: DocKind;
|
|
1307
|
+
/** For carousel slides: place file under slides/ and append SlideRef to target carousel. */
|
|
1308
|
+
asCarouselSlide?: boolean;
|
|
1309
|
+
/** Required when asCarouselSlide:true — the carousel doc whose slides[] should receive the ref. Usually <projectDir>/project.atelier. */
|
|
1310
|
+
carouselDocPath?: string;
|
|
1311
|
+
/** Insertion index for SlideRef (default = append). */
|
|
1312
|
+
insertAt?: number;
|
|
1313
|
+
}
|
|
1314
|
+
interface DuplicateDocOptions {
|
|
1315
|
+
projectDir: string;
|
|
1316
|
+
/** Project-relative path of the doc to duplicate (e.g. "slides/slide-1.atelier" or "scenes/intro.atelier"). */
|
|
1317
|
+
sourceDocPath: string;
|
|
1318
|
+
/** When source is a slide, also append a new SlideRef pointing at the duplicate. */
|
|
1319
|
+
appendSlideRef?: boolean;
|
|
1320
|
+
}
|
|
1321
|
+
interface DeleteDocOptions {
|
|
1322
|
+
workspaceDir: string;
|
|
1323
|
+
projectName: string;
|
|
1324
|
+
/** Project-relative path of the doc to delete. */
|
|
1325
|
+
docPath: string;
|
|
1326
|
+
}
|
|
1327
|
+
interface DocListEntry {
|
|
1328
|
+
/** Project-relative path. */
|
|
1329
|
+
path: string;
|
|
1330
|
+
/** When determinable from the doc's `kind` field; undefined if doc kind is absent or unrecognized. */
|
|
1331
|
+
kind?: "video" | "image" | "carousel";
|
|
1332
|
+
}
|
|
1333
|
+
interface DocDeleteCascadeReport {
|
|
1334
|
+
/** Project-relative path of the doc that was deleted. */
|
|
1335
|
+
docPath: string;
|
|
1336
|
+
/** SlideRefs removed across the workspace. Each entry identifies the carousel project name + the index of the removed ref (PRE-removal index, in case multiple were removed). */
|
|
1337
|
+
slideRefsRemoved: Array<{
|
|
1338
|
+
project: string;
|
|
1339
|
+
slideIndex: number;
|
|
1340
|
+
}>;
|
|
1341
|
+
/** RefVisual layers in any .atelier across the workspace that still point at the deleted doc. These are NOT auto-removed; reported for surfacing as warnings. */
|
|
1342
|
+
refLayersDangling: Array<{
|
|
1343
|
+
project: string;
|
|
1344
|
+
docPath: string;
|
|
1345
|
+
layerId: string;
|
|
1346
|
+
}>;
|
|
1347
|
+
}
|
|
1348
|
+
interface DocDuplicateReport {
|
|
1349
|
+
/** Source doc path (project-relative). */
|
|
1350
|
+
sourceDocPath: string;
|
|
1351
|
+
/** New doc path (project-relative). */
|
|
1352
|
+
newDocPath: string;
|
|
1353
|
+
/** When appendSlideRef was set and the source is a carousel slide: the index of the appended SlideRef. */
|
|
1354
|
+
appendedSlideIndex?: number;
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Aegis surface — same posture as `isValidMediaBasenameForRename` in rename-media.ts.
|
|
1358
|
+
* Validates a user-typed doc display name.
|
|
1359
|
+
*
|
|
1360
|
+
* Rejects:
|
|
1361
|
+
* - empty / whitespace-only
|
|
1362
|
+
* - leading dot (hidden files)
|
|
1363
|
+
* - any `/`, `\`, or `..` substring
|
|
1364
|
+
* - control bytes (NUL through 0x1f)
|
|
1365
|
+
* - longer than 200 characters
|
|
1366
|
+
*
|
|
1367
|
+
* The slug (kebab-case derivative) is computed elsewhere via `slugifyBasename`;
|
|
1368
|
+
* this validator only checks the raw human input.
|
|
1369
|
+
*/
|
|
1370
|
+
declare function isValidDocName(name: unknown): name is string;
|
|
1371
|
+
|
|
1372
|
+
interface CreateDocResult {
|
|
1373
|
+
doc: AtelierDocument;
|
|
1374
|
+
/** Project-relative. */
|
|
1375
|
+
docPath: string;
|
|
1376
|
+
/** Present when the new doc was appended as a SlideRef into a carousel. */
|
|
1377
|
+
appendedSlideIndex?: number;
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Scaffold a new .atelier doc inside a project.
|
|
1381
|
+
*
|
|
1382
|
+
* - Validates `opts.name` via `isValidDocName` (rejects path separators,
|
|
1383
|
+
* `..`, control bytes, etc.).
|
|
1384
|
+
* - Slugs the name via `slugifyBasename` for the filesystem path.
|
|
1385
|
+
* - Collision-suffixes with `-1`, `-2`, ... up to 999.
|
|
1386
|
+
* - For `asCarouselSlide: true`, writes under `slides/<slug>.atelier` AND
|
|
1387
|
+
* calls `composeCarouselProject` so the cycle check + SlideRef append
|
|
1388
|
+
* run as one indivisible step.
|
|
1389
|
+
*/
|
|
1390
|
+
declare function createDoc(opts: CreateDocOptions): Promise<CreateDocResult>;
|
|
1391
|
+
/**
|
|
1392
|
+
* Duplicate an existing doc next to itself with a `-copy` (then `-copy-2`,
|
|
1393
|
+
* `-copy-3`, ...) suffix. Layer ids are preserved verbatim (they're
|
|
1394
|
+
* doc-local — no cross-doc id collision concern).
|
|
1395
|
+
*
|
|
1396
|
+
* `appendSlideRef: true` requires the source to live under `slides/`; we
|
|
1397
|
+
* also call `composeCarouselProject` for the append so the cycle detector
|
|
1398
|
+
* runs defensively even though duplicating a clean slide can't introduce a
|
|
1399
|
+
* new cycle.
|
|
1400
|
+
*/
|
|
1401
|
+
declare function duplicateDoc(opts: DuplicateDocOptions): Promise<DocDuplicateReport>;
|
|
1402
|
+
/**
|
|
1403
|
+
* Delete a doc inside a project AND scrub any workspace-wide references:
|
|
1404
|
+
*
|
|
1405
|
+
* 1. Walk every project subdir of the workspace. For each project, walk
|
|
1406
|
+
* its root + `slides/` for *.atelier files (mirroring listDocs' scope).
|
|
1407
|
+
* 2. For each `.atelier` doc found: scan `slides[]` (if kind:carousel) for
|
|
1408
|
+
* refs that resolve to the absolute path of the doc being deleted;
|
|
1409
|
+
* splice those refs out, persist the updated doc.
|
|
1410
|
+
* 3. Same walk, but scan `layers[]` for RefVisual layers (`visual.type ==
|
|
1411
|
+
* "ref"`) whose `src` resolves to the same absolute path. Collect
|
|
1412
|
+
* these as `refLayersDangling` — do NOT remove them; the renderer
|
|
1413
|
+
* tolerates missing refs and we want the studio to surface dangling
|
|
1414
|
+
* refs as warnings rather than silently nuke them.
|
|
1415
|
+
* 4. Unlink the doc.
|
|
1416
|
+
*
|
|
1417
|
+
* Every .atelier is equal — there is no "project manifest" carve-out.
|
|
1418
|
+
*/
|
|
1419
|
+
declare function deleteDoc(opts: DeleteDocOptions): DocDeleteCascadeReport;
|
|
1420
|
+
/**
|
|
1421
|
+
* Enumerate the .atelier docs belonging to a project for the studio sidebar.
|
|
1422
|
+
*
|
|
1423
|
+
* Post-pivot: every .atelier is equal. Returns a flat alphabetical list of
|
|
1424
|
+
* docs in the project's root + `slides/` subdir. The optional `kind` field
|
|
1425
|
+
* is filled in when the doc parses and has a top-level `kind`; otherwise
|
|
1426
|
+
* omitted.
|
|
1427
|
+
*
|
|
1428
|
+
* Scope is intentionally non-recursive (root + slides/ only) — per Arky's
|
|
1429
|
+
* v1 plan, deeper trees are out of scope for the sidebar.
|
|
1430
|
+
*
|
|
1431
|
+
* Hardening: the `.atelier/` directory at project root (paradigm artifact
|
|
1432
|
+
* sibling) matches `*.atelier` by name but is NOT a doc. We filter by
|
|
1433
|
+
* `statSync().isFile()` AND skip basename `.atelier` defensively.
|
|
1434
|
+
*/
|
|
1435
|
+
declare function listDocs(projectDir: string): DocListEntry[];
|
|
1436
|
+
|
|
1437
|
+
export { ARTIFACT_FILENAMES, type ArtifactParseError, type AssetInfo, type BinIngestRoute, type BuildCaptionsOptions, CanvasUnavailableError, type CaptionStyle, type ComposeCarouselProjectOptions, type ComposeCarouselProjectResult, type ComposeImageProjectOptions, type ComposeImageProjectResult, type ComposeVideoProjectOptions, type ComposeVideoProjectResult, type CreateDocOptions, type CreateDocResult, type DeleteDocOptions, type DocDeleteCascadeReport, type DocDuplicateReport, type DocKind, type DocListEntry, type DocumentInfo, type DuplicateDocOptions, type IngestKind, type IngestRoute, type InitResult, LEARNING_MODES, LEARNING_MODE_VERSION, type LearningMode, type LearningModeFile, type LoadedArtifactProject, type LoadedRecipe, type ProgressInfo, RECIPE_VERSION, type RenderFormat, type RenderOptions, type RenderResult, type RunInitOptions, type TranscribeOptions, type TranscribeResult, type TrimOptions, type TrimResult, VIDEO_CUTLIST_VERSION, VIDEO_PROJECT_VERSION, VIDEO_TRANSCRIPT_VERSION, type VariableInfo, type VideoProject, type WhisperBackend, type WhisperModel, type WhisperOptions, applyAdd, applyBatchReplace, applyHide, applyMerge, applyRecipeCommand, applyRecipeToCaptionOptions, applyRecipeToTranscribeOptions, applyRecipeToTrimOptions, applySplit, applyTextEdit, artifactsCommand, assetsCommand, buildCaptionLayers, buildFfmpegArgs, captionsCommand, carouselCommand, carouselFileName, checkFfmpeg, classifyMediaFile, composeCarouselFrameDoc, composeCarouselProject, composeImageProject, composeVideoProject, createDoc, createVideoProject, deleteDoc, duplicateDoc, effectiveSpan, expandInputs, exportImageCommand, exportLottieCommand, exportSvgCommand, fitImageToCanvas, flattenWords, getAssets, getInfo, getVariables, groupIntoPhrases, infoCommand, initCommand, isInsideGitRepo, isValidDocName, learningModePath, listDocs, loadArtifactsFromProject, loadCanvasModule, loadRecipe, loadVideoProject, mergeTranscriptWithExisting, parseModeAnswer, parseWhisperCppJson, probeWhisper, readComposition, readCutList, readLearningMode, readTranscript, recipeCommand, recipeToYaml, renderCommand, renderDocument, renderDocumentToPng, renderLearningModeYaml, renderRecipeWithDefaults, resolveExportDimensions, resolveRecipePath, resolveStill, rewriteCaptionLayers, rewriteCutLayers, routeIngest, routeIngestWithTarget, runInit, runWhisperCpp, scaffoldRecipeYaml, stillCommand, transcribeCommand, transcribeProject, transcriptCommand, trimCommand, trimProject, validateCommand, validateFile, variablesCommand, writeComposition, writeCutList, writeLearningMode, writeTranscript };
|