Metadata-Version: 2.4
Name: nikokara-cli
Version: 2.0.1
Summary: Safe command-line utilities for a NicoKara workflow
License-Expression: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: tomli>=2.0; python_version < "3.11"
Provides-Extra: schema-test
Requires-Dist: jsonschema<5,>=4.23; extra == "schema-test"
Dynamic: license-file

﻿# nikokara-cli

一个用于学习 CLI-Anything 的隔离实验项目。

## Public distribution status

The current main public release is **2.0.0**, dated **2026-08-27**, and is
published on [official PyPI](https://pypi.org/project/nikokara-cli/2.0.0/).
The unreleased 2.0.1 development line also provides an opt-in
`--diagnostics-json` mode for the business commands that already support
`--json`. This mode is additive: legacy `--json` output remains unchanged.
After argument parsing succeeds and business command execution begins, it emits
one compact JSON envelope with contract version
`nikokara-diagnostics-v1`, a status of `planned`, `succeeded`, `failed`,
`blocked`, or `partial`, and a boolean `ok` value. Exit codes remain `0` for
planned/succeeded, `1` for domain failure/blocked/partial, `2` for argparse
errors before business execution and `70` for an unexpected internal error
after argument parsing; top-level exceptions after execution begins still
produce the envelope with exit code `70`. Argparse parameter or syntax errors
occur before business execution, may write only stderr, and do not require a
diagnostics envelope.
Diagnostics mode writes exactly one envelope to stdout and uses reviewed
allowlists with redacted messages and locations; it does not expose local
paths, commands, credentials, environment variables, raw tool output, or
stack traces. The 2.0.1 line is not published yet.
The main distribution is licensed under the MIT License; see
[`LICENSE`](LICENSE) and [`NOTICE`](NOTICE) for the license text and scope.
The PyPI package and this core-source repository expose only the main
distribution surface. Repository-only tests, development records, sensitive
fixtures, harnesses, skills, and other separately scoped content are not
automatically covered or published just because they share the original
development repository.

## 安全规则

- 未获得用户提供的 exact source-project path 和当前 phase 的 explicit
  authorization，不访问 source project；获得授权后仍默认 read-only。
- 不在 source project 内创建、修改、移动、重命名或删除文件。
- staging、preview 和 render 只使用 source tree 外的新隔离 workspace，并
  只写入受控的 workspace 目录；workspace 输出保持 no-replace。
- public delivery 是独立的最终边界，只能通过 accepted public API 和已验证
  的 delivery authority 执行。
- delivery destination 是 create-only/no-replace，真实 delivery 仅支持
  Windows，不把 authorized destination 当作 render workspace。
- 测试使用 tracked fixtures 或临时隔离 roots，不对 source project 做假设。

## Read-only commands

```bash
nikokara inspect <project> --json
nikokara validate-ass <file.ass> --json
nikokara audit <project> --json
nikokara render-plan <project> --json
```

`render-plan` only reports selected inputs, subtitle font names, output risk,
and suggested FFmpeg arguments. It accepts only the hash-pinned snapshot from
`work/yohane-review.json`; other structurally valid ASS files are never selected
implicitly. It never starts FFmpeg or creates output files.

## New production project

Create a new production directory without touching an existing path:

```bash
# Review the proposed project (no writes)
nikokara init ./my-nikokara-project --name "My Song" --artist "Artist" --json

# Create it after reviewing the proposal
nikokara init ./my-nikokara-project --name "My Song" --artist "Artist" --yes --json
```

`init` is create-only. It refuses an existing target and creates `source`,
`subtitle`, `work`, `preview`, and `output`, plus a UTF-8 `project.toml` that
records the project name, artist, workflow stage, and directory contract.

## Local media import

Copy a selected local video and optional separate audio track into an
initialized project. The source files must be outside the project; imports are
validated with FFprobe and refuse to overwrite an existing import receipt.

```bash
# Review the inputs and their FFprobe metadata without copying files
nikokara import-media ./my-nikokara-project --video /path/to/video.mp4 --audio /path/to/audio.m4a --json

# Copy hash-verified local media and write work/media-import.json
nikokara import-media ./my-nikokara-project --video /path/to/video.mp4 --audio /path/to/audio.m4a --yes --json
```

When provided, video must contain a video stream. A project may also import
only a local audio input for preparation work. Imported files are
stored as `source/video.<ext>` and `source/audio.<ext>`; the receipt records
the selected input paths, copied-file hashes, and stream metadata.

## Media preparation

After a successful import, normalize the audio used for lyric timing. The
command verifies the import receipt and source hash, then creates a 48 kHz,
stereo, 16-bit PCM WAV at `work/audio.wav`. It prefers a separately imported
audio file and otherwise extracts the first audio stream from the imported
video. It also writes `work/media-preparation.json`; neither file is
overwritten.

```bash
nikokara prepare-media ./my-nikokara-project --json
nikokara prepare-media ./my-nikokara-project --yes --json
```

## Yohane alignment job

Stage Yohane outside the production project. The job copies hash-verified
prepared audio and external plain-text lyrics into a new task directory, then
records the exact command and planned ASS output. Yohane can only write inside
that task directory; running it always requires a second explicit confirmation.

```bash
nikokara prepare-yohane-job ./my-nikokara-project /path/to/lyrics.txt /path/to/new-yohane-job --json
nikokara prepare-yohane-job ./my-nikokara-project /path/to/lyrics.txt /path/to/new-yohane-job --yes --json
nikokara run-yohane-job /path/to/new-yohane-job --json
nikokara run-yohane-job /path/to/new-yohane-job --yes --json
nikokara import-yohane-ass ./my-nikokara-project /path/to/new-yohane-job --json
nikokara import-yohane-ass ./my-nikokara-project /path/to/new-yohane-job --yes --json
nikokara confirm-yohane-review ./my-nikokara-project --json
nikokara confirm-yohane-review ./my-nikokara-project --yes --json
nikokara compare-yohane-ass ./my-nikokara-project --json
```

The local Yohane CLI and its shared FFmpeg build are expected under the
configured tool location (or through `NIKOKARA_YOHANE_EXE` and
`NIKOKARA_YOHANE_FFMPEG_BIN`). The generated ASS remains isolated until a later
reviewed import step. `import-yohane-ass` requires a successful Yohane run
report, rechecks the current ASS hash and structure, then copies it once to
`subtitle/yohane.ass`, preserving an immutable original at
`work/yohane-imported.ass` with `work/yohane-import.json`. It never overwrites
an existing subtitle or baseline and its report explicitly records that manual
Aegisub review is still required. You may edit `subtitle/yohane.ass` in
Aegisub; after that review, `confirm-yohane-review --yes` records both the
immutable baseline and reviewed ASS hashes in `work/yohane-review.json`, and
creates a non-overwriting approved snapshot at
`work/approved-subtitles/<reviewed-sha256>.ass`. It is an explicit user
confirmation, not an automated quality claim.

Yohane job lyrics are staged as UTF-8. On Windows, the wrapper enables Python
UTF-8 mode only for the Yohane child process so that this contract remains
stable without changing the parent environment. A `yohane-run.json` report is
single-run evidence: it is created without overwriting an existing report, and
both dry-runs and confirmed runs reject a job that already has one. Create a
new job directory for every retry.

The current Yohane integration does not segment or Romanize Japanese lyrics.
It accepts only a compatible Latin-character alignment transcript, so
nikokara performs the same text-compatibility preflight before creating a Job
and before running an existing Job. Japanese source lyrics, and unsupported
letters, marks, or numbers within otherwise Latin lines, are rejected before
model download rather than silently discarded during alignment. Ordinary
punctuation is treated as a word boundary, so `hello-world` and `hello, world!`
remain two alignment words. Changing the forced-aligner model cannot bypass
this shared text contract. This preflight does not add Japanese support: full
support requires an explicit mapping between display lyrics and an alignment
transcript. The first mapping contract is validated independently and does not
create or run a Yohane Job.

## Lyrics mapping bundle

A Japanese display-lyrics bundle has exactly these files and no additional files
or subdirectories:

```text
lyrics.display.txt
lyrics.alignment.txt
lyrics.mapping.json
```

All three files are UTF-8 without BOM, use LF only, and end with exactly one LF.
The MVP rejects blank or whitespace-only display and alignment lines. Artifacts
must be regular files directly inside the bundle: symlinks and Windows reparse
points are rejected when detected, and resolved paths must remain inside the
bundle root.

`lyrics.mapping.json` is both the mapping and the versioned bundle manifest.
It records the raw SHA-256 values for the display and alignment artifacts, but
never records its own hash or a bundle ID. Its bytes must be canonical JSON v1:
UTF-8 without BOM, `ensure_ascii=true`, `sort_keys=true`, `indent=2`,
`allow_nan=false`, LF-only, and exactly one final LF. Duplicate object keys and
non-finite numbers are rejected. A successful validation returns:

```text
bundle_id = SHA256(canonical lyrics.mapping.json raw bytes)
```

An invalid bundle always returns `bundle_id: null`. Changing either text
artifact requires updating its descriptor in the manifest, which produces new
canonical manifest bytes and therefore a new bundle ID.

The packaged Draft 2020-12 JSON Schema is the public machine-readable
structural contract for editors, external tooling, and conformance CI. The
nikokara-owned validator is the complete runtime authority: it independently
enforces structural rules (required and unknown properties, types, constants,
enums, ID prefixes, provenance, toolchain binding, and conditional timing
policies) as well as artifact hashes, code-point spans, references, ordering,
coverage, and the MVP's 1:1 line/phrase profile. Runtime validation never
imports `jsonschema`.

The optional `schema-test` extra installs `jsonschema` only for standard
CPython conformance environments. Its optional tests verify that the packaged
Schema and owned structural validation stay aligned. Missing that extra never
weakens or changes runtime validation, and core local tests do not require it.

Alignment is canonical lowercase ASCII Latin, with words separated by exactly
one ASCII space. Its timing units must exactly match the owned
`nikokara-yohane-auto-split-2026.5-v1` compatibility splitter, frozen to
Yohane `2026.5.0` source commit
`4b761b0745c6d55661c0f763cbcb579313dd5313`. It is a compatibility contract,
not a general-purpose linguistic mora splitter.

The current MVP profile is `ja-latn-yohane-2026.5-mvp1`: each display line maps
to the alignment line with the same occurrence order; lexical display units and
timing units are consumed exactly once in monotonic projection order. Punctuation
and whitespace are explicit `display_only` units with an attachment policy.
Validation is read-only and reports stable structured diagnostics:

```bash
nikokara validate-lyrics-mapping /path/to/lyrics-bundle --json
nikokara validate-lyrics-mapping /path/to/lyrics-bundle/lyrics.mapping.json --json
```

The command validates a draft only. It does not approve a mapping, create a
receipt, stage a Job v2, invoke Yohane, project ASS, or enter Phase 1B. A
successful validation is not an approval.

### Approval evidence

Approval evidence records an explicit human declaration for one exact lyrics
mapping bundle. Validation success is not approval. The approval commands are
create-only and do not automatically run a Job, projector, Yohane, model, or
media workflow.

```bash
# Review the proposed approval; this is the default and writes nothing
nikokara approve-lyrics-mapping ./lyrics-bundle ./approvals/new-approval \
  --reviewer "Reviewer Name" \
  --declaration reviewed-and-approved --json

# The mapping-file convenience form passes only its parent bundle directory
nikokara approve-lyrics-mapping ./lyrics-bundle/lyrics.mapping.json ./approvals/new-approval \
  --reviewer "Reviewer Name" \
  --declaration reviewed-and-approved --note "Reviewed locally" --yes --json

# Validate approval evidence without modifying it
nikokara validate-lyrics-mapping-approval ./approvals/new-approval --json
```

`--reviewer` and the exact declaration `reviewed-and-approved` are required.
`--note` is optional. Without `--yes` (or with explicit `--dry-run`) approval
creation calls only the write-free plan API; `--yes` is the only create boundary.
The validator command is always read-only and valid, invalid, partial, and
binding-mismatch evidence is reported through its exit code and diagnostics.
JSON output preserves the domain result shape. Human output identifies the
source, destination, creation state, confirmation state, bundle and approval
IDs, paths, and diagnostics.

Created evidence contains exactly:

```text
approved-bundle/
lyrics.mapping.approval.json
```

The destination is no-replace and the validator never repairs or completes
evidence. Reviewer is a recorded declaration field, not identity
authentication, and `approved_at` is local UTC evidence rather than a trusted
timestamp.

### Approval-bound Yohane Job v2

An approval-bound Job v2 stages one validated approval snapshot for alignment;
it is distinct from the legacy plain-lyrics Job v1 command.

```bash
# Plan only (the default and write-free)
nikokara prepare-approved-yohane-job PROJECT APPROVAL JOB --json

# Create one new approval-bound Job v2 after validation
nikokara prepare-approved-yohane-job PROJECT APPROVAL JOB --yes --json
```

`PROJECT` must contain hash-verified prepared audio. `APPROVAL` must be the
complete validated approval root, not `lyrics.mapping.json`; `JOB` must be a
new path outside the project. The default invocation, and explicit `--dry-run`,
only report the plan. `--yes` authorizes only creation of the approval-bound
Job v2, which contains the approved snapshot and alignment materialization.
It does not run Yohane, download a model, generate ASS, project Japanese
mapping output, create a projection receipt, or update `project-status`.

Legacy `prepare-yohane-job` remains the plain alignment-lyrics Job v1 command.
The Job v2 create transaction is no-replace, but a confirmed create failure may
leave an invalid partial Job without a manifest. Such a partial Job must not be
treated as a successfully committed Job and is neither cleaned up nor repaired
by this command.

### Stage 3A synthetic projection core

An internal Stage 3A read-only projection core is available for a strict
synthetic raw ASS profile. It returns projected ASS bytes and projection
evidence in memory only; `production_eligible` is always false. It writes no
files, has no CLI, does not validate a real Yohane execution receipt, and cannot
yet be used on real runs. It creates no projection receipt and has no
project-status, import, or review integration.

### Stage 3B synthetic projection transaction

An internal Stage 3B API can materialize a successful synthetic Stage 3A
projection only at the fixed Job-local `projection/` layout, with
`lyrics.display.ass` and a canonical `lyrics.projection.json` receipt. Planning
is write-free; the create API is the explicit create-only boundary. The receipt
is published last, `projection_id` is the SHA-256 of its exact canonical bytes,
and the validator is read-only. Existing or partial projection roots are
invalid, retained, and never overwritten or repaired. `production_eligible`
remains false; there is no CLI, real-run authority, or project-status, import,
review, or render integration.

`compare-yohane-ass` is read-only and can run before or after confirmation. It
summarizes styles added, removed, or changed, plus subtitle events added,
removed, or changed only in text. Event identity is `kind`, `Layer`, `Start`,
`End`, `Style`, `Name`, `MarginL`, `MarginR`, `MarginV`, and `Effect`; `Text`
is deliberately excluded. A change to any identity field, including timing or
style, is summarized as one removal and one addition rather than a semantic
edit. Repeated events with the same identity are paired in their original
occurrence order. It does not modify, merge, or emit ASS files.

## Project progress

```bash
nikokara project-status ./my-nikokara-project --json
```

`project-status` is read-only. It derives the current phase and next action
from the immutable receipts and their current hash-verified files. A project
may be incomplete and still have an `OK` status; `BLOCKED` means existing
evidence or files no longer agree and needs investigation before progressing.

## Isolated workspace

```bash
# Safe default: inspect the proposed copy only
nikokara prepare-workspace <project> <new-workspace> --json

# Create a new workspace after reviewing the proposal
nikokara prepare-workspace <project> <new-workspace> --yes --json
```

The workspace path must be new, outside the source project, and have an
existing parent directory. The command copies only selected inputs, including
the exact approved subtitle snapshot, and writes their hashes to a manifest; it
never alters the source project or runs FFmpeg.

## Short preview

```bash
# Inspect a one-second preview proposal without rendering
nikokara make-preview <workspace> --seconds 1 --json
nikokara make-preview <workspace> --start-seconds 60 --seconds 1 --json

# Render only into the workspace preview directory
nikokara make-preview <workspace> --seconds 15 --yes --json
```

`make-preview` accepts only a prepared workspace, verifies manifest hashes, and
uses FFmpeg with `-n` to refuse existing outputs. It is limited to 60 seconds.
`--start-seconds` defaults to zero and accepts only non-negative values. For a
non-zero start, the ASS subtitle timeline is applied before the video is
trimmed, so subtitles retain their original timing. The output filename includes
the start time to keep it distinct from a preview beginning at zero.

## Controlled render

```bash
# Review the full render proposal
nikokara render <workspace> --json

# Render only into the workspace output directory
nikokara render <workspace> --yes --json
```

`render` uses only staged, hash-verified inputs and the preplanned output path
from `manifest.json`. It rejects output paths outside `output/`, never
overwrites, and never reads or writes the original project.

## Output verification

```bash
nikokara verify-output <workspace> --json
```

This read-only final check verifies that the rendered MP4 has video and audio
streams and compares its duration with the staged media. No source project is
read during verification.

## Public output delivery

The main `nikokara` CLI exposes the final local delivery boundary after the
render-output authority chain has been accepted:

```bash
# Create one local delivery after reviewing the accepted authorization
nikokara deliver-output JOB --yes
nikokara deliver-output JOB --yes --json

# Validate the retained delivery receipt without publishing again
nikokara validate-delivery JOB
nikokara validate-delivery JOB --json
```

`deliver-output` requires the explicit `--yes` confirmation. It performs the
controlled local, no-replace delivery described by the accepted authorization
and publishes its delivery receipt last, only after the controlled delivery
succeeds. The delivery is Windows-only; on other platforms the command returns
a structured unsupported-platform failure and does not perform a real delivery.

`validate-delivery` is read-only. It validates the durable historical delivery
receipt and does not repair, retry, republish, or read the current destination
state. Neither command bypasses the public delivery APIs to inspect or modify
destination or receipt state.

Both commands print a human-readable summary by default. With `--json`, they
emit the structured domain result, including its action, job, publication state,
receipt and lineage fields when present, plus ordered structured errors when
the operation is invalid. Exit codes are:

- `0`: the public operation returned an `ok` result.
- `1`: the public operation completed with a domain failure, such as an
  unsupported platform, invalid authority, changed filesystem, or invalid
  receipt.
- `2`: command-line parsing or required-argument validation failed.
- `70`: an unexpected public-call exception was contained by the CLI.

The delivery lifecycle is deliberately staged: Stage 4A authorization, Stage
4B controlled execution, Stage 4C technical verification, Stage 4D manual
review, Stage 4E delivery authorization, local delivery execution, and
read-only delivery-receipt validation. The independent
`cli-anything-nikokara` harness does not yet provide delivery-command parity.
