This release is a pre-release and may not be stable for production use.
MetaMuse CLI
Turn comments and transcripts into a recap you can check against the originals.
Have event feedback to review, or a video transcript to quote? MetaMuse keeps your permitted source material, finds matching passages, and saves drafts with their citations. You or your AI assistant writes the recap; MetaMuse keeps the originals and the export together on your computer.
Use it from a terminal, or connect it to an AI assistant that can run CLI or MCP tools. The tool runs on your machine.
Try the example · 繁體中文上手指南 · Connect accounts and publish · Connect your assistant
What can I do with it?
- Review event feedback: find comments about a request, then have your assistant draft a recap with links to the original comments.
- Find a passage in a transcript: import local SRT or VTT subtitles and search for words you want to quote.
- Keep a checkable draft: save your text with source references and export Markdown, text, CSV, JSONL or a bundle.
Search currently matches literal text. Your assistant handles interpretation and writing; a saved citation still needs your review.
Try it
Install uv, then install the alpha package and run the bundled example. uv manages the Python version and creates an isolated environment for the command.
uv tool install --python 3.12.13 'metamuse-cli==0.1.0a1'
metamuse demo
Installation downloads Python and dependencies. The example itself runs locally with synthetic comments and a prewritten recap. It needs no account, Muse connection or AI service.
You will import three records, search for 字幕 (subtitles), and export a Markdown recap with the matching comment and its source reference. The terminal prints Export directory:; open the .md files there to see the result. This is a demonstration of the data workflow, not an AI-generated analysis.
Every run creates a separate workspace outside the checkout, under your system's user data directory (metamuse/demos/). It stays there so you can inspect it. Delete the workspace printed by the example when you no longer need it.
The getting started guide (繁體中文) shows the expected result and how to move on to your own files. The sample inputs are in src/metamuse/data/examples/event-comments.jsonl.
Use it with your AI assistant
Connect the installed CLI using the Skill setup guide, or configure the MCP server. Loading the Skill supplies instructions; your assistant also needs access to the working CLI or MCP backend.
Once connected, try a request like this:
Use MetaMuse to review the event comments I provide and have permission to use. Find feedback about subtitles, group the requests, and draft a recap with links to the original comments. Show me the sources and gaps before saving. Export Markdown and keep this work local.
Muse is an optional collaborator for further questions. Local import, search, drafts and exports work without a Muse account. A real Muse connection needs separate configuration and human approval; the Muse guide covers that path. Real-account LIVE verification is still pending.
Use metamuse connect muse, connect facebook, or connect publishing to validate your
private credential file and bind the account without editing TOML. Add --enable-network
to explicitly enable network use; each external action still needs its own approval.
The connected workflow guide (中文) walks through setup,
collecting one post, Muse follow-ups, a cited draft and Page publication. This complete
sequence has synthetic integration coverage; real-account acceptance is pending.
What can I use today?
Start with files on your computer. You can search, save drafts and export without a Muse account. Your assistant handles interpretation and writing once connected.
| What you want to do | Availability | What you get |
|---|---|---|
| Import saved comments and subtitles | Ready with local files | Bring together the saved comments, articles and subtitle files you already have, so you can find them later. |
| Find a comment or transcript passage | Ready with local files | Search for a word such as subtitles, then read the matching text and its source. |
| Keep a draft with its sources | Ready with local files | Save text you or your assistant wrote, together with the original material you cited. |
| Export a file you can edit | Ready with local files | Keep your draft and material as text documents or spreadsheet-friendly files for further editing. |
| Work with your AI assistant | Connect your assistant first | Connect your assistant to MetaMuse, then ask it to find material and draft a recap with citations. |
| Ask Muse follow-up questions | Needs extra setup; real-account use is unverified | Set up your Muse connection and approve what may be shared before asking questions. Real-account use has not been verified. |
| Collect a Facebook post or remote video | Needs extra setup; real-account use is unverified | Additional setup and your approval are required. These collectors have not been verified with real accounts. |
| Publish a text post to a Facebook Page | Needs extra setup; real-account use is unverified | Preview a saved draft, approve its exact text and Page, then send and check the saved receipt. Text-only Page posts are experimental; setup and limits. |
See the getting started guide to try the local example, or connect your assistant to use your own material. Developers can find the implementation and verification inventory in the development documentation.
Advanced: ask Muse follow-up questions with one bounded approval
The implemented bounded research workflow lets a human approve a fixed packet, topic, expiry and question/read budget. Your assistant can then ask varied questions within that scope, keep the discussion across restarts, and save a draft citing sources and Muse observations. Changed data or scope needs new consent. This has synthetic test coverage; it does not establish real-account LIVE success.
Advanced: full command-by-command offline example, including fixture Muse
Local offline walkthrough
Run from the repository root after uv sync --python 3.12. This synthetic walkthrough collects data, previews a packet, opens a fixture Muse discussion, asks a question and a follow-up in the same discussion, saves host-written text and exports five formats. The host shows the human each E_SCOPE_REQUIRED approval preview. Only the human runs operator permit approve, reviews its stderr (including expiry and uses), and repeats the unchanged call with the returned grant. The agent never approves. Evidence is OFFLINE/FIXTURE, with no network or accounts.
These Bash blocks run in order. Python only edits configuration and copies public JSON fields; every product operation uses the shipped CLI. tests/test_readme_walkthrough.py executes these exact blocks in a temporary workspace.
export UV_OFFLINE=1
mkdir -p "$HOME/agent-work/tmp"
export SLICE_DIR="${SLICE_DIR:-$(mktemp -d "$HOME/agent-work/tmp/metamuse-at042-XXXXXX")}"
export SLICE_WS="$SLICE_DIR/workspace"
mkdir "$SLICE_DIR/inputs"
mm() { uv run metamuse --workspace "$SLICE_WS" --json --non-interactive "$@"; }
operator() { uv run metamuse --workspace "$SLICE_WS" --json "$@"; }
field() { uv run python -c 'import json,sys; print(json.load(open(sys.argv[1]))["data"][sys.argv[2]])' "$1" "$2"; }
approval_field() { uv run python -c 'import json,sys; print(json.load(open(sys.argv[1]))["approval"][sys.argv[2]])' "$1" "$2"; }
preview() {
local output="$1"; shift
local result=0
mm "$@" > "$output" || result=$?
test "$result" -eq 3
uv run python -c 'import json,sys; e=json.load(open(sys.argv[1])); assert e["error"]["code"] == "E_SCOPE_REQUIRED" and e["approval"]; print(e["approval"]["summary"]); print(e["approval"]["next_action"])' "$output"
}
save_request() {
uv run python -c 'import json,sys; a=json.load(open(sys.argv[1]))["approval"]; json.dump(a["request"],open(a["request_file"],"w"),ensure_ascii=False)' "$1"
}
mm init > "$SLICE_DIR/init.json"
uv run python - <<'PYTHON'
import json, os
from pathlib import Path
root = Path(os.environ['SLICE_DIR'])
config = Path(os.environ['SLICE_WS']) / 'metamuse.toml'
text = config.read_text().replace('[app]\n', '[app]\nimport_roots = ' + json.dumps([str(root / 'inputs')]) + '\n')
text = text.replace('[muse]\nenabled = false', '[muse]\nenabled = true\nbackend = "fixture"')
config.write_text(text)
corpus = Path('tests/fixtures/corpus/import/comments_tree.jsonl')
(root / 'inputs/comments.jsonl').write_text('\n'.join(corpus.read_text().splitlines()[:3]) + '\n')
PYTHON
mm doctor > "$SLICE_DIR/doctor.json"
mm project create --name 'Synthetic slice' --request-key project > "$SLICE_DIR/project.json"
export SLICE_PROJECT=$(field "$SLICE_DIR/project.json" id)
mm add --project "$SLICE_PROJECT" --file "$SLICE_DIR/inputs/comments.jsonl" --kind jsonl --request-key import --run > "$SLICE_DIR/import.json"
mm search --project "$SLICE_PROJECT" --query '這是' > "$SLICE_DIR/search.json"
uv run python - <<'PYTHON'
import json, os
from pathlib import Path
root = Path(os.environ['SLICE_DIR'])
hits = json.loads((root / 'search.json').read_text())['data']['results']
(root / 'inputs/selection.json').write_text(json.dumps([hit['ref'] for hit in hits]))
PYTHON
mm packet prepare --project "$SLICE_PROJECT" --selection-file "$SLICE_DIR/inputs/selection.json" --request-key packet > "$SLICE_DIR/packet.json"
export SLICE_PACKET=$(field "$SLICE_DIR/packet.json" id)
mm packet preview "$SLICE_PACKET" > "$SLICE_DIR/packet-preview.json"
cat "$SLICE_DIR/packet-preview.json"
Call without a grant, show the review and its human command, save the public request, then approve the exact digest. Credential-like values are redacted separately in every text field. A redacted or edited request fails the original digest; it must be reviewed again. Expired, revoked, exhausted or mismatched grants return E_SCOPE_DENIED without a preview. Repeat without a grant to get a fresh review.
preview "$SLICE_DIR/open-preview.json" muse open --project "$SLICE_PROJECT" --topic 'Synthetic activity review' --request-key open
save_request "$SLICE_DIR/open-preview.json"
operator permit approve --operation muse.open --request-file "$(approval_field "$SLICE_DIR/open-preview.json" request_file)" --exact-digest "$(approval_field "$SLICE_DIR/open-preview.json" action_digest)" --request-key open-grant > "$SLICE_DIR/open-grant.json"
mm muse open --project "$SLICE_PROJECT" --topic 'Synthetic activity review' --access-grant "$(field "$SLICE_DIR/open-grant.json" id)" --request-key open > "$SLICE_DIR/open.json"
export SLICE_DISCUSSION=$(field "$SLICE_DIR/open.json" discussion_id)
mm jobs get "$(field "$SLICE_DIR/open.json" job_id)"
mm worker run --once
mm jobs get "$(field "$SLICE_DIR/open.json" job_id)"
printf '%s' 'List evidence and gaps.' > "$SLICE_DIR/inputs/question-1.txt"
printf '%s' 'Which missing replies would change those conclusions?' > "$SLICE_DIR/inputs/question-2.txt"
for exchange in 1 2; do
preview "$SLICE_DIR/send-preview-$exchange.json" muse send --discussion "$SLICE_DISCUSSION" --packet "$SLICE_PACKET" --question-file "$SLICE_DIR/inputs/question-$exchange.txt" --request-key "send-$exchange"
mm packet preview "$SLICE_PACKET"
save_request "$SLICE_DIR/send-preview-$exchange.json"
operator permit approve --operation muse.send --request-file "$(approval_field "$SLICE_DIR/send-preview-$exchange.json" request_file)" --exact-digest "$(approval_field "$SLICE_DIR/send-preview-$exchange.json" action_digest)" --request-key "share-grant-$exchange" > "$SLICE_DIR/share-grant-$exchange.json"
mm muse send --discussion "$SLICE_DISCUSSION" --packet "$SLICE_PACKET" --question-file "$SLICE_DIR/inputs/question-$exchange.txt" --share-grant "$(field "$SLICE_DIR/share-grant-$exchange.json" id)" --request-key "send-$exchange" > "$SLICE_DIR/send-$exchange.json"
export SLICE_EXCHANGE=$(field "$SLICE_DIR/send-$exchange.json" exchange_id)
mm jobs get "$(field "$SLICE_DIR/send-$exchange.json" job_id)"
mm worker run --once
mm jobs get "$(field "$SLICE_DIR/send-$exchange.json" job_id)"
preview "$SLICE_DIR/read-preview-$exchange.json" muse read --exchange "$SLICE_EXCHANGE" --refresh
save_request "$SLICE_DIR/read-preview-$exchange.json"
operator permit approve --operation muse.read --request-file "$(approval_field "$SLICE_DIR/read-preview-$exchange.json" request_file)" --exact-digest "$(approval_field "$SLICE_DIR/read-preview-$exchange.json" action_digest)" --request-key "read-grant-$exchange" > "$SLICE_DIR/read-grant-$exchange.json"
mm muse read --exchange "$SLICE_EXCHANGE" --refresh --access-grant "$(field "$SLICE_DIR/read-grant-$exchange.json" id)" > "$SLICE_DIR/refresh-$exchange.json"
mm worker run --once
mm muse read --exchange "$SLICE_EXCHANGE" > "$SLICE_DIR/read-$exchange.json"
mm muse close-exchange "$SLICE_EXCHANGE" --outcome needs_more_data --request-key "close-$exchange" > "$SLICE_DIR/close-$exchange.json"
done
The fixture replies are progress, so the operator closes each exchange as needs_more_data. Draft text comes from the host and cites observations from both exchanges. CLI invocations have already restarted the application between steps; the separate MCP acceptance test also restarts a real stdio server and checks that no send repeats.
uv run python - <<'PYTHON'
import json, os
from pathlib import Path
root = Path(os.environ['SLICE_DIR'])
facts = json.loads((root / 'inputs/selection.json').read_text())
citations = []
for exchange in (1, 2):
read = json.loads((root / f'read-{exchange}.json').read_text())['data']
assistant_ids = {message['id'] for message in read['messages'] if message['role'] == 'assistant'}
observed = next(o for o in read['observations'] if o['message_id'] in assistant_ids and o['raw_event'].get('payload',{}).get('role') == 'assistant')
assert read['mock'] and read['research_complete'] is False
facts.append(observed['evidence_ref'])
citations.append(observed['id'])
(root / 'inputs/facts.json').write_text(json.dumps(facts))
host_text = '=活動回顧(host 撰寫)\r\n首輪列出缺口;追問指出仍需要留言與子回覆。😀 e\u0301\n'
(root / 'inputs/draft.txt').write_bytes((host_text + '\n引用:' + ', '.join(citations) + '\n').encode())
PYTHON
mm draft save --project "$SLICE_PROJECT" --text-file "$SLICE_DIR/inputs/draft.txt" --platform generic --facts-file "$SLICE_DIR/inputs/facts.json" --request-key draft > "$SLICE_DIR/draft.json"
export SLICE_DRAFT=$(field "$SLICE_DIR/draft.json" draft_id)
mm draft preview "$SLICE_DRAFT" --revision 1 > "$SLICE_DIR/preview.json"
uv run python - <<'PYTHON'
import json, os
from pathlib import Path
(Path(os.environ['SLICE_DIR']) / 'inputs/export-refs.json').write_text(json.dumps([{'kind':'draft_revision','id':os.environ['SLICE_DRAFT'],'revision':1}]))
PYTHON
for format in bundle jsonl markdown text csv; do
mm export --project "$SLICE_PROJECT" --refs-file "$SLICE_DIR/inputs/export-refs.json" --format "$format" --request-key "export-$format" > "$SLICE_DIR/export-$format.json"
done
cat "$SLICE_DIR/preview.json" "$SLICE_DIR/export-bundle.json"
if [ "${SLICE_KEEP:-0}" != 1 ]; then rm -rf -- "$SLICE_DIR"; fi
Skill
The canonical Skill tree is skills/metamuse/. The wheel ships the same files, and metamuse skill export --dest DIR writes them to a directory the operator names. It is not an MCP tool. Installation steps are in SKILL.md. The Skill requires an installed, reachable MetaMuse backend (CLI or MCP server) to execute operations; when the backend is unavailable, operations cannot be simulated.
Development and project details
One package (metamuse) and one shared application serve the CLI, MCP server and Skill. See the design baseline, core API, collection formats, and full offline walkthrough.
For development, use Python 3.12 managed by uv:
pytest-disktmp uv run pytest -q
uv run ruff check .
Runtime workspaces belong outside the checkout. Credentials, account settings, real collected data and LIVE evidence stay outside this repository and are supplied by the operator at runtime.
This is an unofficial project, unaffiliated with Meta, Facebook or Muse. It does not claim official API access, terms-of-service compliance or protection against account restrictions.
License: Apache-2.0. Dependency notices: THIRD_PARTY_NOTICES.md.
Metadata
Release files for metamuse-cli 0.1.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| metamuse_cli-0.1.0a1.tar.gz | 4.8 MB | Without provenance | Details for metamuse_cli-0.1.0a1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| metamuse_cli-0.1.0a1-py3-none-any.whl | Python 3 | none | any | Details for metamuse_cli-0.1.0a1-py3-none-any.whl |
Total release size: 5.2 MB
Release files / metamuse_cli-0.1.0a1.tar.gz
| Download URL | metamuse_cli-0.1.0a1.tar.gz |
|---|---|
| Size | 4.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a1fae9ca2cf93726ac646642e7a4aaf129dc6bae99d9700adcfc9b1e1efa7bd9
|
|
BLAKE2b-256 checksum How to use checksums |
5b834a6eaefc7316bdb747f2f0422ca7026fabae2cf0c74c4a72be4518d70337
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.20 {"installer":{"name":"uv","version":"0.11.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / metamuse_cli-0.1.0a1-py3-none-any.whl
| Download URL | metamuse_cli-0.1.0a1-py3-none-any.whl |
|---|---|
| Size | 429.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
784d86c4b4fdaf96ce49b63c15c4a856cab88893ac5813b2e90e4dc89ba677b3
|
|
BLAKE2b-256 checksum How to use checksums |
19d83ba9ea92cccd30bf379aa70410d844214a0a6b87d69b4272f105b31450fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.20 {"installer":{"name":"uv","version":"0.11.20","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"26.04","id":"resolute","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for metamuse-cli 0.1.0a1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.