diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 52e872d..89b9c6d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,5 @@ .github/ @nvidia-nemo/automation profile/ @bernardwin @ericharper @euronymous-aithal @snowmanwwg README.md @bernardwin @ericharper @euronymous-aithal @snowmanwwg +scripts/ @nvidia-nemo/automation +rotation/ @nvidia-nemo/automation diff --git a/.github/workflows/rotate-profile-readme.yml b/.github/workflows/rotate-profile-readme.yml new file mode 100644 index 0000000..5c732ca --- /dev/null +++ b/.github/workflows/rotate-profile-readme.yml @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +name: Rotate org profile README + +on: + schedule: + # Mondays 14:00 UTC. The first run of each month is the first Monday, which starts + # that month's rotation; later runs only refresh the catalog and reuse the selection. + - cron: "0 14 * * 1" + pull_request: + # Exercises the script on a real runner; never commits. + paths: + - ".github/workflows/rotate-profile-readme.yml" + - "scripts/**" + - "rotation/**" + - "profile/**" + workflow_dispatch: + inputs: + dry_run: + description: "Print the selection without committing" + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: rotate-profile-readme + cancel-in-progress: false + +jobs: + rotate: + runs-on: ubuntu-latest + env: + PUSH: ${{ github.ref == 'refs/heads/main' && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.dry_run)) }} + steps: + # The push to main is made by the nemo-automation-bot app, which is allowed to + # bypass the pull request requirement. GITHUB_TOKEN stays read-only. + - name: Create bot token + id: bot + if: ${{ env.PUSH == 'true' }} + uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0 + with: + app-id: ${{ vars.BOT_ID }} + private-key: ${{ secrets.BOT_KEY }} + permission-contents: write + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + token: ${{ steps.bot.outputs.token || github.token }} + + - name: Render README and catalog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DRY_RUN: ${{ env.PUSH != 'true' && '1' || '' }} + run: python3 scripts/rotate_readme.py ${DRY_RUN:+--dry-run} + + - name: Commit and push + if: ${{ env.PUSH == 'true' }} + env: + GH_TOKEN: ${{ steps.bot.outputs.token }} + BOT_SLUG: ${{ steps.bot.outputs.app-slug }} + run: | + git add profile rotation + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + bot_id=$(gh api "/users/${BOT_SLUG}[bot]" --jq .id) + git config user.name "${BOT_SLUG}[bot]" + git config user.email "${bot_id}+${BOT_SLUG}[bot]@users.noreply.github.com" + git commit -m "chore: rotate org profile README ($(date -u +%Y-%m))" + for attempt in 1 2 3; do + git push && exit 0 + echo "Push attempt ${attempt} failed; rebasing on origin/${GITHUB_REF_NAME}" + git fetch origin "${GITHUB_REF_NAME}" + git rebase "origin/${GITHUB_REF_NAME}" + done + echo "Push failed after 3 attempts" >&2 + exit 1 diff --git a/profile/README.md b/profile/README.md new file mode 100644 index 0000000..9ee7e0d --- /dev/null +++ b/profile/README.md @@ -0,0 +1,33 @@ +### Featured repositories ยท November 2026 + + + + + + + + + + + + + + +
+

NeMo Safe Synthesizer

+

๐Ÿ›ก๏ธ NeMo Safe Synthesizer: Create private, safe versions of sensitive tabular datasets.

+
+

NeMo Switchyard

+

Switchyard lets LLM applications route traffic across models and providers while preserving native OpenAI and Anthropic API compatibility - enabling flexible model selection, benchmarking, and cost/performance optimization.

+
+

NeMo Eval Author

+

Research preview: skills that help your coding assistant build evaluations for custom agents, assess the quality of existing suites, and find behavior-coverage gaps. Works today with NeMo Gym and Harbor.

+
+

NeMo Labs Molt

+

A scalable, agentic-first, and HuggingFace-native RL framework for research (9k lines).

+
+

NeMo Lens

+
+

Nemotron

+

Developer Asset Hub for NVIDIA Nemotron โ€” A one-stop resource for training recipes, usage cookbooks, datasets, and full end-to-end reference examples to build with Nemotron models

+
diff --git a/profile/README.template.md b/profile/README.template.md new file mode 100644 index 0000000..6f09105 --- /dev/null +++ b/profile/README.template.md @@ -0,0 +1,3 @@ +### Featured repositories ยท {{month_year}} + +{{featured_cards_html}} diff --git a/profile/assets/categories/nemo-agents.svg b/profile/assets/categories/nemo-agents.svg new file mode 100644 index 0000000..d515307 --- /dev/null +++ b/profile/assets/categories/nemo-agents.svg @@ -0,0 +1 @@ +NeMo AgentsNeMo Agents diff --git a/profile/assets/categories/nemo-data.svg b/profile/assets/categories/nemo-data.svg new file mode 100644 index 0000000..c7c31de --- /dev/null +++ b/profile/assets/categories/nemo-data.svg @@ -0,0 +1 @@ +NeMo DataNeMo Data diff --git a/profile/assets/categories/nemo-evaluation.svg b/profile/assets/categories/nemo-evaluation.svg new file mode 100644 index 0000000..4a99938 --- /dev/null +++ b/profile/assets/categories/nemo-evaluation.svg @@ -0,0 +1 @@ +NeMo EvaluationNeMo Evaluation diff --git a/profile/assets/categories/nemo-safety.svg b/profile/assets/categories/nemo-safety.svg new file mode 100644 index 0000000..8257dad --- /dev/null +++ b/profile/assets/categories/nemo-safety.svg @@ -0,0 +1 @@ +NeMo SafetyNeMo Safety diff --git a/profile/assets/categories/nemo-training.svg b/profile/assets/categories/nemo-training.svg new file mode 100644 index 0000000..d3d5480 --- /dev/null +++ b/profile/assets/categories/nemo-training.svg @@ -0,0 +1 @@ +NeMo TrainingNeMo Training diff --git a/profile/assets/nemo-header-tm.png b/profile/assets/nemo-header-tm.png new file mode 100644 index 0000000..478cd19 Binary files /dev/null and b/profile/assets/nemo-header-tm.png differ diff --git a/profile/repositories.json b/profile/repositories.json new file mode 100644 index 0000000..7524c3a --- /dev/null +++ b/profile/repositories.json @@ -0,0 +1,887 @@ +{ + "schema_version": 1, + "organization": "NVIDIA-NeMo", + "generated_at": "2026-11-07T01:31:30.722820+00:00", + "source_url": "https://api.github.com/orgs/NVIDIA-NeMo/repos", + "scope": "All public repositories, including archived repos and repos excluded from promotion.", + "field_notes": { + "categories": "Derived only from the five documented nemo category topics. Empty means unclassified.", + "research_preview": "True means the nemo-research-preview topic is present. Null means unspecified, not stable or production-ready.", + "homepage_url": "Repository owner supplied homepage; it is not necessarily documentation.", + "license": "GitHub-reported license metadata, not a review of repository terms. Null or NOASSERTION means no specific license was identified by GitHub. A license with source 'manual override' was set from rotation/config.json where GitHub could not match the repo's license file.", + "excluded_from_featured": "True if the repo has the nemo-exclude-featured topic or is excluded in rotation/config.json. Promotion exclusion only. Does not remove the repo from this catalog." + }, + "category_topics": { + "nemo-data": "NeMo Data", + "nemo-training": "NeMo Training", + "nemo-evaluation": "NeMo Evaluation", + "nemo-agents": "NeMo Agents", + "nemo-safety": "NeMo Safety" + }, + "repositories": [ + { + "name": ".github", + "full_name": "NVIDIA-NeMo/.github", + "description": null, + "repository_url": "https://github.com/NVIDIA-NeMo/.github", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/.github/readme", + "homepage_url": null, + "license": null, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "agent-hardener", + "full_name": "NVIDIA-NeMo/agent-hardener", + "description": null, + "repository_url": "https://github.com/NVIDIA-NeMo/agent-hardener", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/agent-hardener/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "Anonymizer", + "full_name": "NVIDIA-NeMo/Anonymizer", + "description": "๐Ÿ•ต๏ธ NeMo Anonymizer: Detect and protect PII through context-aware replacement and rewriting", + "repository_url": "https://github.com/NVIDIA-NeMo/Anonymizer", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Anonymizer/readme", + "homepage_url": "https://nvidia-nemo.github.io/Anonymizer/latest/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "anonymization-pipeline", + "contextual-privacy", + "llm", + "nvidia", + "nvidia-nemo", + "pii-anonymization", + "sdg" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Automodel", + "full_name": "NVIDIA-NeMo/Automodel", + "description": "๐Ÿš€ Pytorch Distributed native training library for LLMs/VLMs with OOTB Hugging Face support", + "repository_url": "https://github.com/NVIDIA-NeMo/Automodel", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Automodel/readme", + "homepage_url": "https://docs.nvidia.com/nemo/automodel/nightly", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "agent", + "deepseek-v4", + "deepseek-v4-flash", + "finetuning", + "gemma3", + "gemma4", + "glm", + "gpt-oss", + "kimi-k3", + "llama", + "llama3", + "llm", + "minimax-m2", + "mistral", + "openai", + "qwen-38-27b", + "qwen3", + "qwen3-next", + "vlm" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Curator", + "full_name": "NVIDIA-NeMo/Curator", + "description": "Scalable data pre processing and curation toolkit for LLMs", + "repository_url": "https://github.com/NVIDIA-NeMo/Curator", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Curator/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "data", + "data-curation", + "data-prep", + "data-preparation", + "data-processing", + "data-processing-pipelines", + "data-quality", + "datacuration", + "datarecipes", + "deduplication", + "fast-data-processing", + "fine-tuning", + "large-language-models", + "large-scale-data-processing", + "llm", + "llm-data-quality", + "llmapps", + "python", + "semantic-deduplication" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "DataDesigner", + "full_name": "NVIDIA-NeMo/DataDesigner", + "description": "๐ŸŽจ NeMo Data Designer: Generate high-quality synthetic data from scratch or from seed data.", + "repository_url": "https://github.com/NVIDIA-NeMo/DataDesigner", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/DataDesigner/readme", + "homepage_url": "https://docs.nvidia.com/nemo/datadesigner", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "agentic-ai", + "data-augmentation", + "data-generation", + "llm", + "mcp", + "multimodal", + "nemo", + "nvidia", + "sdg", + "synthetic-data", + "tool-use" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "DataDesignerPlugins", + "full_name": "NVIDIA-NeMo/DataDesignerPlugins", + "description": "๐Ÿ”Œ ๐ŸŽจ NeMo Data Designer Plugins", + "repository_url": "https://github.com/NVIDIA-NeMo/DataDesignerPlugins", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/DataDesignerPlugins/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "DFM", + "full_name": "NVIDIA-NeMo/DFM", + "description": "State-of-the-art framework for fast, large-scale training and inference of diffusion models", + "repository_url": "https://github.com/NVIDIA-NeMo/DFM", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/DFM/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": true, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "Emerging-Optimizers", + "full_name": "NVIDIA-NeMo/Emerging-Optimizers", + "description": null, + "repository_url": "https://github.com/NVIDIA-NeMo/Emerging-Optimizers", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Emerging-Optimizers/readme", + "homepage_url": "https://docs.nvidia.com/nemo/emerging-optimizers/latest/index.html", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "eval-author", + "full_name": "NVIDIA-NeMo/eval-author", + "description": "Research preview: skills that help your coding assistant build evaluations for custom agents, assess the quality of existing suites, and find behavior-coverage gaps. Works today with NeMo Gym and Harbor.", + "repository_url": "https://github.com/NVIDIA-NeMo/eval-author", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/eval-author/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Evaluator", + "full_name": "NVIDIA-NeMo/Evaluator", + "description": "Open-source library for scalable, reproducible evaluation of AI models and benchmarks.", + "repository_url": "https://github.com/NVIDIA-NeMo/Evaluator", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Evaluator/readme", + "homepage_url": "https://docs.nvidia.com/nemo/evaluator/latest/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "Export-Deploy", + "full_name": "NVIDIA-NeMo/Export-Deploy", + "description": "A library for exporting models including NeMo and Hugging Face to optimized inference backends, and deploying them for efficient querying", + "repository_url": "https://github.com/NVIDIA-NeMo/Export-Deploy", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Export-Deploy/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "FW-CI-templates", + "full_name": "NVIDIA-NeMo/FW-CI-templates", + "description": "CI/CD templates for NeMo-FW libraries", + "repository_url": "https://github.com/NVIDIA-NeMo/FW-CI-templates", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/FW-CI-templates/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "Guardrails", + "full_name": "NVIDIA-NeMo/Guardrails", + "description": "NeMo Guardrails is an open-source toolkit for easily adding programmable guardrails to LLM-based conversational systems.", + "repository_url": "https://github.com/NVIDIA-NeMo/Guardrails", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Guardrails/readme", + "homepage_url": "https://docs.nvidia.com/nemo/guardrails/latest/index.html", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "source": "manual override", + "note": "LICENSE.md declares Apache-2.0 (SPDX-License-Identifier header and the standard short notice); GitHub reports NOASSERTION because it cannot match the file to a standard license." + }, + "default_branch": "develop", + "topics": [ + "agents", + "generative-ai", + "guardrails", + "llm-safety", + "llm-security", + "llms", + "nvidia", + "python", + "safety" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Gym", + "full_name": "NVIDIA-NeMo/Gym", + "description": "Evaluate and improve models and agents using environments", + "repository_url": "https://github.com/NVIDIA-NeMo/Gym", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Gym/readme", + "homepage_url": "https://docs.nvidia.com/nemo/gym/main/about/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "agents", + "benchmarks", + "environments", + "evaluation", + "gym", + "llm", + "reinforcement-learning", + "reinforcement-learning-environments", + "rl-environment", + "rl-training" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "labs-molt", + "full_name": "NVIDIA-NeMo/labs-molt", + "description": "A scalable, agentic-first, and HuggingFace-native RL framework for research (9k lines).", + "repository_url": "https://github.com/NVIDIA-NeMo/labs-molt", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/labs-molt/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "agentic-rl", + "context-parallelism", + "expert-parallelism", + "fsdp", + "huggingface", + "llm", + "rl-framework", + "rlhf", + "rlvr", + "tensor-parallelism" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "labs-OO-Agents", + "full_name": "NVIDIA-NeMo/labs-OO-Agents", + "description": "NVIDIA Object Oriented Agents: the Pythonic way to build AI Agents.", + "repository_url": "https://github.com/NVIDIA-NeMo/labs-OO-Agents", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/labs-OO-Agents/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "source": "manual override", + "note": "LICENSE declares Apache-2.0 (full Apache License 2.0 text plus an appended copyright notice); GitHub reports NOASSERTION because it cannot match the file to a standard license." + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "labs-Voice-Agent", + "full_name": "NVIDIA-NeMo/labs-Voice-Agent", + "description": "A fully opensource framework for building and evaluating voice agents with open models. ", + "repository_url": "https://github.com/NVIDIA-NeMo/labs-Voice-Agent", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/labs-Voice-Agent/readme", + "homepage_url": "https://docs.nvidia.com/nemo/labs-voice-agent/about/overview/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Lens", + "full_name": "NVIDIA-NeMo/Lens", + "description": null, + "repository_url": "https://github.com/NVIDIA-NeMo/Lens", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Lens/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Megatron-Bridge", + "full_name": "NVIDIA-NeMo/Megatron-Bridge", + "description": "Training library for Megatron-based models with bidirectional Hugging Face conversion capability", + "repository_url": "https://github.com/NVIDIA-NeMo/Megatron-Bridge", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Megatron-Bridge/readme", + "homepage_url": "https://docs.nvidia.com/nemo/megatron-bridge/latest/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "nemo-compass", + "full_name": "NVIDIA-NeMo/nemo-compass", + "description": "Research preview: Tools to efficiently find signals in large sets of agent traces and inform agent improvements.", + "repository_url": "https://github.com/NVIDIA-NeMo/nemo-compass", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/nemo-compass/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "nemo-helix", + "full_name": "NVIDIA-NeMo/nemo-helix", + "description": "NeMo Helix: Make the agents you ship faster, more accurate, and safer.", + "repository_url": "https://github.com/NVIDIA-NeMo/nemo-helix", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/nemo-helix/readme", + "homepage_url": "https://docs.nvidia.com/nemo-helix", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "agent-evaluation", + "ai-agents", + "guardrails", + "kubernetes", + "llm", + "llmops", + "nemo", + "nvidia", + "python", + "red-teaming" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Nemotron", + "full_name": "NVIDIA-NeMo/Nemotron", + "description": "Developer Asset Hub for NVIDIA Nemotron โ€” A one-stop resource for training recipes, usage cookbooks, datasets, and full end-to-end reference examples to build with Nemotron models", + "repository_url": "https://github.com/NVIDIA-NeMo/Nemotron", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Nemotron/readme", + "homepage_url": "https://docs.nvidia.com/nemotron/latest/index.html", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "ai", + "fine-tuning", + "model-training", + "nemotron", + "nvidia", + "reinforcement-learning" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "ProRL-Agent-Server", + "full_name": "NVIDIA-NeMo/ProRL-Agent-Server", + "description": "Agentic RL on Any Harness at Scale", + "repository_url": "https://github.com/NVIDIA-NeMo/ProRL-Agent-Server", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/ProRL-Agent-Server/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "stable", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "RL", + "full_name": "NVIDIA-NeMo/RL", + "description": "Scalable toolkit for efficient model reinforcement", + "repository_url": "https://github.com/NVIDIA-NeMo/RL", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/RL/readme", + "homepage_url": "https://docs.nvidia.com/nemo/rl/latest/index.html", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Run", + "full_name": "NVIDIA-NeMo/Run", + "description": "A tool to configure, launch and manage your machine learning experiments.", + "repository_url": "https://github.com/NVIDIA-NeMo/Run", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Run/readme", + "homepage_url": "https://docs.nvidia.com/nemo/run/latest/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Safe-Synthesizer", + "full_name": "NVIDIA-NeMo/Safe-Synthesizer", + "description": "๐Ÿ›ก๏ธ NeMo Safe Synthesizer: Create private, safe versions of sensitive tabular datasets.", + "repository_url": "https://github.com/NVIDIA-NeMo/Safe-Synthesizer", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Safe-Synthesizer/readme", + "homepage_url": "https://nvidia-nemo.github.io/Safe-Synthesizer/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "SDG-PGMs", + "full_name": "NVIDIA-NeMo/SDG-PGMs", + "description": "NeMo SDG-PGMs is a user-friendly Python library for building probabilistic graphical models (PGMs) to generate synthetic data", + "repository_url": "https://github.com/NVIDIA-NeMo/SDG-PGMs", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/SDG-PGMs/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": true + }, + { + "name": "Skills", + "full_name": "NVIDIA-NeMo/Skills", + "description": "A project to improve skills of large language models", + "repository_url": "https://github.com/NVIDIA-NeMo/Skills", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Skills/readme", + "homepage_url": "https://nvidia-nemo.github.io/Skills/", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Speech", + "full_name": "NVIDIA-NeMo/Speech", + "description": "A scalable generative AI framework built for researchers and developers working on Large Language Models, Multimodal, and Speech AI (Automatic Speech Recognition and Text-to-Speech)", + "repository_url": "https://github.com/NVIDIA-NeMo/Speech", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Speech/readme", + "homepage_url": "https://docs.nvidia.com/nemo/speech/nightly/index.html", + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "asr", + "deeplearning", + "generative-ai", + "machine-translation", + "neural-networks", + "speaker-diariazation", + "speaker-recognition", + "speech-synthesis", + "speech-translation", + "tts" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "Switchyard", + "full_name": "NVIDIA-NeMo/Switchyard", + "description": "Switchyard lets LLM applications route traffic across models and providers while preserving native OpenAI and Anthropic API compatibility - enabling flexible model selection, benchmarking, and cost/performance optimization.", + "repository_url": "https://github.com/NVIDIA-NeMo/Switchyard", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/Switchyard/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + }, + { + "name": "UserSim", + "full_name": "NVIDIA-NeMo/UserSim", + "description": "๐ŸŽญ NeMo User Sim: Improve models and agents with population-grounded simulated users, even before production traffic exists.", + "repository_url": "https://github.com/NVIDIA-NeMo/UserSim", + "readme_api_url": "https://api.github.com/repos/NVIDIA-NeMo/UserSim/readme", + "homepage_url": null, + "license": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "url": "https://api.github.com/licenses/apache-2.0", + "node_id": "MDc6TGljZW5zZTI=" + }, + "default_branch": "main", + "topics": [ + "dynamic-evaluation", + "multilingual-ai", + "nemo", + "nvidia", + "personas", + "sdg", + "simulation", + "sovereign-ai", + "synthetic-data", + "user-simulator" + ], + "categories": [], + "research_preview": null, + "archived": false, + "disabled": false, + "fork": false, + "excluded_from_featured": false + } + ] +} diff --git a/rotation/config.json b/rotation/config.json new file mode 100644 index 0000000..800e578 --- /dev/null +++ b/rotation/config.json @@ -0,0 +1,31 @@ +{ + "cards_per_month": 6, + "excluded": { + ".github": "Org infrastructure", + "FW-CI-templates": "Org infrastructure", + "DFM": "Archived", + "agent-hardener": "Excluded by request, 2026-10-29", + "DataDesignerPlugins": "Excluded by request, 2026-10-29", + "Export-Deploy": "Excluded by request, 2026-10-29", + "Evaluator": "Excluded by request, 2026-10-29", + "SDG-PGMs": "No pushes since 2026-09-28" + }, + "display_names": { + "Automodel": "NeMo AutoModel", + "Nemotron": "Nemotron" + }, + "license_overrides": { + "Guardrails": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "note": "LICENSE.md declares Apache-2.0 (SPDX-License-Identifier header and the standard short notice); GitHub reports NOASSERTION because it cannot match the file to a standard license." + }, + "labs-OO-Agents": { + "key": "apache-2.0", + "name": "Apache License 2.0", + "spdx_id": "Apache-2.0", + "note": "LICENSE declares Apache-2.0 (full Apache License 2.0 text plus an appended copyright notice); GitHub reports NOASSERTION because it cannot match the file to a standard license." + } + } +} diff --git a/rotation/state.json b/rotation/state.json new file mode 100644 index 0000000..c524a61 --- /dev/null +++ b/rotation/state.json @@ -0,0 +1,47 @@ +{ + "order": [ + "Curator", + "RL", + "Automodel", + "Megatron-Bridge", + "Guardrails", + "Gym", + "Safe-Synthesizer", + "Switchyard", + "eval-author", + "labs-molt", + "Lens", + "Nemotron", + "labs-Voice-Agent", + "nemo-helix", + "nemo-compass", + "Skills", + "UserSim", + "DataDesigner", + "Anonymizer", + "Emerging-Optimizers", + "Speech", + "Run", + "labs-OO-Agents", + "ProRL-Agent-Server" + ], + "cursor": 12, + "history": { + "2026-10": [ + "Curator", + "RL", + "Automodel", + "Megatron-Bridge", + "Guardrails", + "Gym" + ], + "2026-11": [ + "Safe-Synthesizer", + "Switchyard", + "eval-author", + "labs-molt", + "Lens", + "Nemotron" + ] + } +} diff --git a/scripts/rotate_readme.py b/scripts/rotate_readme.py new file mode 100644 index 0000000..3131500 --- /dev/null +++ b/scripts/rotate_readme.py @@ -0,0 +1,264 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Refresh the org repository catalog and render the monthly profile README. + +Reads rotation/config.json (hand-edited) and rotation/state.json (machine-edited), +fetches public repo metadata, picks this month's featured repos and writes +profile/README.md and profile/repositories.json. Standard library only. +""" + +import argparse +import datetime +import html +import json +import os +from pathlib import Path +import re +import sys +import urllib.request + + +ORG = "NVIDIA-NeMo" +CATEGORIES = { + "nemo-data": "NeMo Data", + "nemo-training": "NeMo Training", + "nemo-evaluation": "NeMo Evaluation", + "nemo-agents": "NeMo Agents", + "nemo-safety": "NeMo Safety", +} +EXCLUDE_TOPIC = "nemo-exclude-featured" +SEARCH = "https://github.com/search?q=org%3A{org}+topic%3A{topic}+is%3Apublic+archived%3Afalse&type=repositories" + + +def fetch_repos(token): + repos, page = [], 1 + while True: + url = f"https://api.github.com/orgs/{ORG}/repos?type=public&per_page=100&page={page}" + headers = {"Accept": "application/vnd.github+json", "User-Agent": "nemo-org-profile-rotation"} + if token: + headers["Authorization"] = f"Bearer {token}" + with urllib.request.urlopen(urllib.request.Request(url, headers=headers), timeout=30) as response: + batch = json.load(response) + repos.extend(batch) + if len(batch) < 100: + return repos + page += 1 + + +def to_entry(repo): + topics = sorted(repo.get("topics", [])) + return { + "name": repo["name"], + "full_name": repo["full_name"], + "description": repo["description"], + "repository_url": repo["html_url"], + "readme_api_url": f'https://api.github.com/repos/{repo["full_name"]}/readme', + "homepage_url": repo.get("homepage") or None, + "license": repo.get("license"), + "default_branch": repo["default_branch"], + "topics": topics, + "categories": [label for topic, label in CATEGORIES.items() if topic in topics], + "research_preview": True if "nemo-research-preview" in topics else None, + "archived": repo["archived"], + "disabled": repo["disabled"], + "fork": repo["fork"], + "excluded_from_featured": EXCLUDE_TOPIC in topics, + } + + +def build_catalog(entries, previous): + catalog = { + "schema_version": 1, + "organization": ORG, + "generated_at": datetime.datetime.now(datetime.timezone.utc).isoformat(), + "source_url": f"https://api.github.com/orgs/{ORG}/repos", + "scope": "All public repositories, including archived repos and repos excluded from promotion.", + "field_notes": previous["field_notes"], + "category_topics": CATEGORIES, + "repositories": sorted(entries, key=lambda item: item["full_name"].lower()), + } + # Keep the old timestamp when nothing else changed, so unchanged runs make no commit. + if {k: v for k, v in catalog.items() if k != "generated_at"} == {k: v for k, v in previous.items() if k != "generated_at"}: + catalog["generated_at"] = previous["generated_at"] + return catalog + + +def is_eligible(entry, config): + return not ( + entry["archived"] or entry["disabled"] or entry["fork"] + or entry["excluded_from_featured"] or entry["name"] in config["excluded"] + ) + + +def select(period, state, by_name, config): + """Return this period's featured names, updating state in place. + + A new period takes the eligible repos in the next `cards_per_month` positions of + the order and always advances the cursor by that many, so later batches never + shift. Any shortfall, from ineligible repos or a mid-month replacement, is filled + from the positions after the window without moving the cursor. Such a repo is + featured again in its own slot. A period already in history is reused. + """ + count = config["cards_per_month"] + eligible = {name for name, entry in by_name.items() if is_eligible(entry, config)} + order = state["order"] + order.extend(sorted(eligible - set(order), key=str.lower)) + if not order: + raise ValueError("no eligible repositories to feature") + + if period in state["history"]: + chosen = [n for n in state["history"][period] if n in eligible] + else: + start = state["cursor"] % len(order) + window = [order[(start + step) % len(order)] for step in range(count)] + chosen = list(dict.fromkeys(n for n in window if n in eligible)) + state["cursor"] = (start + count) % len(order) + + cursor = state["cursor"] % len(order) + for step in range(len(order)): + if len(chosen) >= count: + break + name = order[(cursor + step) % len(order)] + if name in eligible and name not in chosen: + chosen.append(name) + state["history"][period] = chosen + return chosen + + +def display_name(name, config): + if name in config["display_names"]: + return config["display_names"][name] + words = [w if w != w.lower() else w.capitalize() for w in re.split(r"[-_]", name)] + if words[0].lower() == "nemo": + words[0] = "NeMo" + return " ".join(words) + return "NeMo " + " ".join(words) + + +def render_card(entry, config): + url = html.escape(entry["repository_url"], quote=True) + lines = [ + ' ', + f'

{html.escape(display_name(entry["name"], config))}

', + ] + if entry["description"]: + lines.append(f'

{html.escape(entry["description"])}

') + chips = [ + f'' + f'{label}' + for topic, label in CATEGORIES.items() if label in entry["categories"] + ] + if chips: + lines.append(f'


{" ".join(chips)}

') + lines.append(" ") + return "\n".join(lines) + + +def render_cards(names, by_name, config): + cells = [render_card(by_name[n], config) for n in names] + if len(cells) % 2: + cells.append(' ') + rows = [f" \n{cells[i]}\n{cells[i + 1]}\n " for i in range(0, len(cells), 2)] + return "\n" + "\n".join(rows) + "\n
" + + +def placeholder(values, key): + if key not in values: + raise ValueError(f"unknown template placeholder {{{{{key}}}}}") + return values[key] + + +def validate(readme, names, config, root): + for ref in re.findall(r'src="(assets/[^"]+)"', readme): + if not (root / "profile" / ref).is_file(): + raise ValueError(f"missing asset {ref}") + for name in config["excluded"]: + if f"/{ORG}/{name}\"" in readme: + raise ValueError(f"excluded repo {name} is featured") + if readme.count("") != len(names): + raise ValueError("card count does not match selection") + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parent.parent) + parser.add_argument("--date", type=datetime.date.fromisoformat, default=datetime.datetime.now(datetime.timezone.utc).date()) + parser.add_argument("--offline", action="store_true", help="use profile/repositories.json instead of the API") + parser.add_argument("--dry-run", action="store_true", help="print the selection without writing files") + args = parser.parse_args() + root = args.root + + today = datetime.datetime.now(datetime.timezone.utc).date() + if not args.dry_run and (args.date.year, args.date.month) != (today.year, today.month): + raise ValueError(f"--date {args.date} is not in the current UTC month; use --dry-run to preview other months") + + config = json.loads((root / "rotation/config.json").read_text()) + state = json.loads((root / "rotation/state.json").read_text()) + previous = json.loads((root / "profile/repositories.json").read_text()) + + if args.offline: + entries = previous["repositories"] + else: + entries = [to_entry(r) for r in fetch_repos(os.environ.get("GITHUB_TOKEN"))] + for entry in entries: + entry["excluded_from_featured"] = EXCLUDE_TOPIC in entry["topics"] or entry["name"] in config["excluded"] + override = config.get("license_overrides", {}).get(entry["name"]) + current = entry["license"] + # Only fill gaps: never replace a license GitHub identified. + if override and (not current or current["spdx_id"] == "NOASSERTION"): + entry["license"] = { + "key": override["key"], + "name": override["name"], + "spdx_id": override["spdx_id"], + "url": f"https://api.github.com/licenses/{override['key']}", + "source": "manual override", + "note": override["note"], + } + by_name = {e["name"]: e for e in entries} + catalog = build_catalog(entries, previous) + + period = args.date.strftime("%Y-%m") + names = select(period, state, by_name, config) + if not names: + raise ValueError("selection is empty; refusing to publish an empty README") + month_year = args.date.strftime("%B %Y") + cards = render_cards(names, by_name, config) + template = (root / "profile/README.template.md").read_text() + values = {"month_year": month_year, "featured_cards_html": cards} + # One pass, so braces inside repo descriptions are never treated as placeholders. + readme = re.sub(r"\{\{(\w+)\}\}", lambda m: placeholder(values, m.group(1)), template) + validate(readme, names, config, root) + + print(f"{month_year}: {', '.join(names)}") + if args.dry_run: + return + outputs = { + "profile/README.md": readme, + "profile/repositories.json": json.dumps(catalog, ensure_ascii=False, indent=2) + "\n", + "rotation/state.json": json.dumps(state, indent=2) + "\n", + } + for relative, text in outputs.items(): + destination = root / relative + staging = destination.with_suffix(destination.suffix + ".tmp") + staging.write_text(text) + staging.replace(destination) + + +if __name__ == "__main__": + try: + main() + except Exception as error: + print(f"error: {error}", file=sys.stderr) + sys.exit(1)