Metadata-Version: 2.5
Name: transcript-weaver
Version: 1.0.11
Summary: A safe, Unix-style transcript processing pipeline
Project-URL: Homepage, https://github.com/sec-engineers/transcript-weaver
Project-URL: Repository, https://github.com/sec-engineers/transcript-weaver
Project-URL: Issues, https://github.com/sec-engineers/transcript-weaver/issues
Project-URL: Documentation, https://github.com/sec-engineers/transcript-weaver/tree/main/docs
Project-URL: Changelog, https://github.com/sec-engineers/transcript-weaver/blob/main/CHANGELOG.md
Author: Transcript Weaver contributors
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Keywords: cli,otter,pipeline,transcripts
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: platformdirs<5,>=4.0
Requires-Dist: playwright<2,>=1.45
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8.2; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# Transcript Weaver

Transcript Weaver is a Python 3.10+ Unix-style pipeline for safely acquiring,
transforming, and filing transcripts:

## Installation

Install Transcript Weaver with all currently advertised functionality, including
Otter support:

```bash
python3 -m pip install transcript-weaver
```

For local development from a repository checkout:

```bash
python3 -m pip install --editable .
```

Otter acquisition currently supports WSL on Windows and controls the user's existing
Windows Chrome through Chrome DevTools integration. Browser automation is intentional:
Otter currently reserves its supported public API for Enterprise workspaces. The
Playwright Python dependency is installed with Transcript Weaver, but Weaver does not
require Playwright to download or maintain a separate bundled browser. Windows Chrome,
WSL-to-Windows connectivity, a dedicated Chrome profile, and one-time port-proxy setup
are still required; see
[`docs/otter.md`](https://github.com/sec-engineers/transcript-weaver/blob/main/docs/otter.md)
for the rationale and setup
instructions before attempting `trwinp otter`.

```text
trwinp -> normalized JSON -> trweave -> enriched JSON -> trwout
```

```bash
trwinp otter | trweave franks-example | trwout franks-example
```

Standard output is reserved for JSON packets. Diagnostics and errors use standard
error. Ordinary successful operation is silent except that `trwinp` and `trweave`
emit their packet. `trwout` writes a file and emits no normal output.

## Packet contract

`trwinp` emits schema version 1, the creating project version, and a canonical UTC recording time:

```json
{
  "schema_version": 1,
  "trw_version": "1.0.0001",
  "run": {"id": "20260805-120000-a1b2"},
  "datetime": "2026-08-05T08:30:00Z",
  "source": {"type": "otter", "name": "Morning note", "reference": "https://otter.ai/u/example"},
  "transcript": "Speaker 1 10:00 : Um, I am grateful this moring Transcribe by https://otter.ai",
  "metadata": {"duration_seconds": 180.0}
}
```

`trweave` deeply verifies that all those fields remain unchanged and permits the
provider to add fields. A valid result includes:

```json
{
  "schema_version": 1,
  "trw_version": "1.0.0001",
  "run": {"id": "20260805-120000-a1b2"},
  "datetime": "2026-08-05T08:30:00Z",
  "source": {"type": "otter", "name": "Morning note", "reference": "https://otter.ai/u/example"},
  "transcript": "Speaker 1 10:00 : Um, I am grateful this moring Transcribe by https://otter.ai",
  "metadata": {"duration_seconds": 180.0},
  "weave": {"type": "gratitude", "update_transcript": "- I am grateful for this morning."}
}
```

The same valid `run.id` survives every stage. A missing ID is generated by any stage,
and a malformed ID is rejected. The `trw_version` field is informational bug-report
metadata, not a compatibility gate: downstream stages preserve it when present but
accept packets created by any version, including legacy packets without the field.
When Otter exposes a reliable media duration, `trwinp` records it as
`metadata.duration_seconds`. Franks Example deterministically routes values greater than
300 seconds to `unknown`; exactly 300 seconds remains content-classified.

## Configuration

On first use, the packaged schema-v1 configuration and prototype-derived
`prompts/example.md` are copied atomically to the platform's per-user configuration
directory. Existing files are never merged, migrated, repaired, or overwritten. Tests
always inject disposable paths.

Older configuration files are not automatically migrated. If you want to replace an
early prototype configuration with the complete example, first back it up and then run
a command so Transcript Weaver can create the new defaults:

```bash
mv ~/.config/transcript-weaver/config.json ~/.config/transcript-weaver/config.json.backup
printf 'configuration check' | trwinp stdin > /tmp/transcript-weaver-packet.json
```

Alternatively, add the fields named by the validation error to your existing file. The
program will never overwrite your real-vault settings.

A coherent configuration looks like this:

```json
{
  "schema_version": 1,
  "logging": {"retained_runs": 5},
  "providers": {
    "gemini": {
      "model": "gemini-3.5-flash-lite",
      "credential": {"source": "pass", "name": "api/gemini"}
    }
  },
  "weave": {
    "franks-example": {
      "provider": "gemini",
      "prompt_file": "prompts/example.md"
    }
  },
  "out": {
    "franks-example": {
      "timezone": "America/Los_Angeles",
      "vault": {
        "path": "transcript-weaver-test-output",
        "relative_to": "cwd"
      },
      "packet_fields": {
        "category": "weave.type",
        "content": "weave.update_transcript"
      },
      "destination_roots": {
        "journals": "SubDir1/SubDir2"
      },
      "destinations": {
        "gratitude": {
          "operation": "insert",
          "root": "journals",
          "file": "Gratitude Journal.md",
          "format": "## {date}\n\n{content}\n\n"
        },
        "dream": {
          "operation": "insert",
          "root": "journals",
          "file": "Dream Journal.md",
          "format": "## {date}\n\n{content}\n\n"
        },
        "ses": {
          "operation": "insert",
          "root": "journals",
          "file": "SEs Journal.md",
          "format": "## {date}\n\n{content}\n\n"
        },
        "sacred": {
          "operation": "insert",
          "root": "journals",
          "file": "Sacred Journey.md",
          "format": "## {date}\n\n{content}\n\n"
        },
        "unknown": {
          "operation": "create",
          "directory": "00 Inbox",
          "filename": "unknown-{date}-{time}.md",
          "format": "## {date}\n\n{content}\n\n"
        }
      }
    }
  }
}
```

Provider, weave, output, and destination names are matched case-insensitively.
Names differing only by case are rejected.

Path objects use `path` plus `relative_to: "cwd"` or `relative_to: "config"` for
relative paths. For an absolute path such as `/mnt/d/Notes` or a home-relative path
such as `~/Obsidian/Notes`, omit `relative_to`. Existing path strings remain supported
for compatibility. Keys beginning with `_comment` are embedded documentation and are
ignored; other unexpected keys are rejected.

Each output profile’s `vault` is the common root for all its destinations.
An optional `destination_roots` object defines reusable relative paths beneath the
resolved vault. A destination selects a root with its explicit `root` field; root
names are matched case-insensitively, and names differing only by case are rejected.
Unknown root names produce an error listing the available roots. Placeholder forms
such as `{journals}` are not supported.

Resolution with a selected root is:

```text
vault + destination root + destination file/directory
```

When `root` is omitted, the backward-compatible resolution is:

```text
vault + destination file/directory
```

Destination roots, files, and directories must be relative. They cannot escape the
resolved vault with `..` or by resolving through a link to a location outside it.
In the example, the four journals are under `vault/SubDir1/SubDir2`; the unrooted unknown
destination remains under the vault-level `00 Inbox`.

## Weaving

Need an LLM API but do not have one yet? See
[Free and Low-Cost LLM APIs](https://github.com/sec-engineers/transcript-weaver/blob/main/docs/free-and-low-cost-llm-apis.md)
for general
information about free tiers, low-cost gateways, and important trade-offs.
Transcript Weaver does not currently support every provider discussed there.

`trweave PROMPT_OR_PROFILE` first treats its argument as a path, resolving a
relative path from the current working directory. A readable UTF-8 regular file
supplies the complete prompt and uses the configured Gemini provider. Otherwise,
the argument is resolved as a weave profile. No extension guessing or file
searching occurs.

Gemini credentials are read from `pass`; secrets never belong in configuration.
The implementation uses Gemini's REST API directly, requests JSON, and retries
transient network errors and HTTP 429/5xx responses up to four times, waiting 1,
4, 9, and 16 seconds. Every retry is reported on standard error and is also
written to the persistent run log when `--log` is enabled. Every input field
is immutable. If the provider replaces `transcript`, Weaver preserves the original
and uses that attempted replacement only when needed to populate
`weave.update_transcript`; no duplicate top-level transformed-text field is emitted. Other deleted or modified input fields remain errors. Fenced, explanatory,
partial, non-object, or invalid `weave` responses are rejected.

## Output

`trwout OUTPUT_PROFILE` extracts category and content through the configured
dotted packet fields. It converts the packet's UTC datetime to the profile's IANA
timezone without modifying the packet. `{date}`, `{time}`, and `{content}` are the
only formatting placeholders.

- `insert` atomically places date-only headings in ascending local-date order.
- Multiple entries on the same local date are retained in arrival order, separated by
  `---`, and a warning identifying the date is written to standard error.
- `append` atomically appends the configured rendered text.
- `create` creates a standalone file atomically and refuses to overwrite one.

The example stores `Dream Journal.md`, `Gratitude Journal.md`, `SEs Journal.md`,
and `Sacred Journey.md` beneath the configured vault-relative journal root. Unknown
material is routed to the vault-relative `00 Inbox` directory as
`unknown-{date}-{time}.md`. The packaged prompt asks for approximately 72-character
word-boundary wrapping while keeping long-form unknown content substantially verbatim.

## Logging and privacy

All commands accept `--log`, `--verbose`, and `--debug-artifacts`. Logs are
optional, grouped by run ID, and retained according to `logging.retained_runs`.
They may contain stage, profile, model, operation, relative destination, timing,
and sanitized failure information. Ordinary logs never contain transcripts, prompts,
transformed content, provider bodies, credentials, or complete journals.

If a provider changes or deletes an immutable packet field, `trweave` writes the
complete original and provider JSON documents under
`<log-directory>/packet-failures/`. These files are intentionally sufficient for a
manual diff and therefore may contain complete transcripts and other private packet
data. Inspect them before sharing. They are grouped by run ID and bounded by
`logging.retained_runs`; the current failure is always retained. Debug artifacts are
also potentially sensitive.

## Project version and distribution builds

`src/transcript_weaver/_version.py` is the single source of truth for the project
version used by package metadata, `transcript_weaver.__version__`, and the packet
field `trw_version`.
The public format is `major.minor.build`, with a four-digit build component. Python packaging canonicalizes numeric release components, so application version `1.0.0001` appears in wheel/sdist filenames and Core Metadata as the PEP 440-equivalent `1.0.1`; the packaged `_version.py` and emitted packets retain `1.0.0001`.

Create distributions with:

```bash
python -m transcript_weaver.build
```

That intentional command takes an exclusive build lock, increments the build component
once, atomically updates the source version, and builds both distributions. It restores
the previous source version if the build fails. Imports, tests, installs, and ordinary
packaging metadata inspection do not increment the version. A stale lock must be
investigated and removed manually after confirming no build is active.

## Development and tests

```bash
pytest -m 'not live_otter and not live_gemini'
ruff check .
ruff format --check .
mypy
python -m transcript_weaver.build
```

The default suite is offline and uses fake providers and miniature temporary
vaults. Live Otter and Gemini checks are opt-in. After all offline checks pass:

```bash
TRANSCRIPT_WEAVER_LIVE_GEMINI=1 pytest --no-cov -m live_gemini tests/test_live_gemini.py
```

The live Otter functional test uses the configured WSL/Windows Chrome session:

```bash
TRANSCRIPT_WEAVER_LIVE_OTTER=1 pytest --no-cov -m live_otter tests/test_live_otter.py
```

That command reads `pass api/gemini` and sends only the harmless test transcript
to Gemini. It does not access an Obsidian vault or Otter.

## License

Copyright © 2026 Frank Merrow.

Transcript Weaver is free software licensed under the GNU General Public License,
version 3 or later. See the
[LICENSE](https://github.com/sec-engineers/transcript-weaver/blob/main/LICENSE)
file for the complete terms.
