engineering / Build & debug
port
Port an upstream GitHub repository into a fresh private destination repository with a new identity, clean initial history, detailed docs, CI/pre-commit/Flox setup, structure cleanup, and upstream-reference sanitization.
Put it to work.
Ask your agent to use port, or let it load the skill when your task matches the description.
Use the port skill <SOURCE-GITHUB-URL>Invocation syntax varies by agent. Use the skill name in your prompt, or your agent’s supported slash command.
About this skill
Portable /port skill for turning an upstream GitHub repository into a fresh
private destination repository with a new identity, clean history, documentation,
tooling, and validation.
- Inspect the source repo and destination access through
gh. - Require an explicitly approved, unique new destination repo name.
- Audit source structure, tooling, docs, CI, legal notices, large files, flat directories, and upstream identity references.
- Create a private destination repo without preserving upstream Git history.
- Sanitize upstream identity, improve structure, split large implementation files where useful, and write detailed docs.
- Slim the README, lowercase docs filenames, add useful Mermaid diagrams, update badges, and install LOC/flat-directory budget gates.
- Ask for Blacksmith or GitHub-hosted runners; share CI and hook checks through Flox, use Bun where compatible, and validate Markdown and Mermaid. Apply the bundled license exactly and configure retry-safe automatic releases.
- Validate and push one fresh initial commit, then monitor CI and releases until green, adding focused repair commits if needed.
Lifecycle
flowchart LR
A[Intake] --> B[Name Gate]
B --> C[Audit]
C --> D[Plan]
D --> E[Initialize]
E --> F[Sanitize]
F --> G[Restructure]
G --> H[Document]
H --> I[Harden]
I --> J[Tool]
J --> K[Validate]
K --> L[Commit]
L --> N[Monitor CI and releases]
N --> M[Handoff]
N --> KThe skill is stateful and resumable. Re-running the same invocation resumes from
~/.claude/port/<source-owner>-<source-repo>.json, while GitHub and the local
filesystem remain the source of truth.
stateDiagram-v2
[*] --> Intake
Intake --> NameGate
NameGate --> Audit: user approves name
Audit --> Plan
Plan --> Initialize
Initialize --> Sanitize
Sanitize --> Restructure
Restructure --> Document
Document --> Harden
Harden --> Tool
Tool --> Validate
Validate --> Commit
Commit --> Monitor
Monitor --> Validate: repair failed checks
Monitor --> Handoff: CI and releases green
Handoff --> [*]
Intake --> Blocked: missing access or legal issue
NameGate --> Blocked: no approved name
Validate --> Blocked: unresolved failing gateInstall
Via the smeltery skills CLI flow:
npx skills@latest add smeltery/skillsOr copy just this skill:
mkdir -p ~/.claude/skills/port
curl -fsSL https://raw.githubusercontent.com/smeltery/skills/main/skills/engineering/port/SKILL.md \
-o ~/.claude/skills/port/SKILL.mdUsage
/port https://github.com/source-owner/source-repoThe skill asks for the destination owner when it cannot be inferred, then stops until the user approves the new repository name.
Requirements
gitghCLI authenticated against GitHub- Permission to read the source repository
- Permission to create or initialize a private destination repository
- Local build/test tools required by the source project
Files
SKILL.md— canonical skill definition consumed by the agent.assets/LICENSE— exact license copied into ported projects.
Read the full SKILL.md
Port: private repo productization driver
Idempotent, state-branched skill for turning an upstream repository into a fresh private repository under a user-approved new name.
Same invocation drives: Intake → Name gate → Audit → Plan → Initialize → Sanitize → Restructure → Document → Harden → Tool → Validate → Commit → Handoff.
Arguments: "$ARGUMENTS"
0. Parse argument
Extract <SOURCE-GITHUB-URL>. It must be a GitHub repository URL such as
https://github.com/<owner>/<repo>. If the argument is missing or malformed,
abort with a short error telling the user the expected form.
Destination owner/org is discovered from the user request or asked for during Intake. Do not assume the destination owner unless the user stated it.
1. Load state
State file: ~/.local/share/port/<source-owner>-<source-repo>.json.
{
"sourceUrl": "https://github.com/source/repo",
"sourceSlug": "source/repo",
"destinationOwner": "example-org",
"destinationName": null,
"destinationSlug": null,
"workspacePath": "/absolute/path/to/scratch/repo",
"baseBranch": "main",
"phase": "intake|name|audit|plan|initialize|sanitize|restructure|document|harden|tool|validate|commit|handoff",
"legalHold": false,
"nameApproved": false,
"repoCreated": false,
"runnerProvider": null,
"ciRunIds": [],
"finalCommitSha": null
}Create ~/.local/share/port/ if needed. If no state exists, start at Intake. If
state exists, jump to its phase, then re-check local files and GitHub before
continuing. State is a cache; GitHub and the filesystem are source of truth.
2. Route
if no state file OR phase == "intake": run Intake (§3)
elif phase == "name": run Name Gate (§4)
elif phase == "audit": run Source Audit (§5)
elif phase == "plan": run Migration Plan (§6)
elif phase == "initialize": run Repository Initialization (§7)
elif phase == "sanitize": run Identity Sanitization (§8)
elif phase == "restructure": run Structure Cleanup (§9)
elif phase == "document": run Documentation (§10)
elif phase == "harden": run Documentation and Budget Hardening (§11)
elif phase == "tool": run Tooling (§12)
elif phase == "validate": run Validation (§13)
elif phase == "commit": run Final Commit (§14)
elif phase == "handoff": run Handoff (§15)3. Intake phase
3.1 Verify GitHub access
Use gh, not unauthenticated scraping, for repository metadata:
gh repo view "$SOURCE_SLUG" \
--json nameWithOwner,description,isPrivate,isArchived,defaultBranchRef,licenseInfoAlso confirm the destination owner/org can create private repositories:
gh repo view "$DESTINATION_OWNER/placeholder" >/dev/null 2>&1 || true
gh auth statusIf source access, destination access, or gh auth is missing, stop and tell the
user exactly what access is needed.
3.2 Identify source facts
Determine the default branch, license/notice obligations, language stack, package/build/test systems, monorepo shape, submodules/LFS/generated/vendored assets, release artifacts, CI, pre-commit, docs, and dev environment tooling.
3.3 Legal gate
Read license files and notice files before sanitizing identity. If the license
requires attribution or preserving notices, set legalHold: true, summarize the
obligation, and stop for user approval. Do not silently remove legally required
notices.
3.4 Runner choice
Ask whether to use Blacksmith or GitHub-hosted Actions runners unless the user
already chose. Record runnerProvider in state and confirm available labels,
platforms, and organization access before writing workflows. Both choices use
GitHub Actions; do not silently substitute providers.
3.5 Destination license
Copy assets/LICENSE byte-for-byte to the destination LICENSE.
Do not substitute a similarly named standard license, change the copyright, or
mention the template's originating project in generated files or the handoff.
Preserve required upstream and third-party notices separately. If the source
license conflicts with applying this license, stop and explain the conflict;
user approval alone does not remove license obligations.
Set phase: "name" when Intake is complete.
4. Name Gate
The destination repo name must be different, unique, and explicitly approved by the user before cloning for migration, creating the destination repo, or making commits.
4.1 Ask for a name
Ask the user for the new repository name. If useful, suggest several names based on the domain, but do not choose for them.
Name requirements:
- Different from the source repository name.
- Available under the destination owner/org.
- Does not mention or strongly resemble the source owner or source repo.
- Suitable for package, docs, badge, and release identity.
4.2 Check availability
Use gh:
gh repo view "$DESTINATION_OWNER/$DESTINATION_NAME"If the repo exists, ask whether to use that existing private repo only if it is empty and the user explicitly approves. Otherwise ask for a new name.
Set nameApproved: true and phase: "audit" only after explicit user
approval.
5. Source Audit
Clone the source into a scratch location only after the name gate passes. Keep the upstream remote local to the scratch repo and never reuse upstream history for the final destination.
git clone "$SOURCE_URL" "$WORKSPACE_PATH"
cd "$WORKSPACE_PATH"
git fetch --all --tagsAudit and record: top-level map, subprojects, build/test/lint/typecheck/docs commands, CI runners, pre-commit, Flox/Nix/devcontainer/asdf/mise files, README coverage, contributor docs, files over 1000 LOC, flat directories, and source identity references. Categorize large files as implementation, generated, vendored, fixture, data, lockfile, or docs.
Use explicit searches:
rg -n "$SOURCE_OWNER|$SOURCE_REPO|github.com/$SOURCE_OWNER/$SOURCE_REPO" .
find . -type f | while read -r file; do wc -l "$file"; done | sort -nr | headSet phase: "plan" when the audit is complete.
6. Migration Plan
Write a concise but concrete migration plan before making destructive or large-scale changes.
The plan must cover new repo/package/binary/module/docs/release identity, directories to reorganize, >1000 LOC files to split or justify, docs to add or rewrite, CI/pre-commit/Flox changes, validation commands, final search terms, and legal notices that must remain.
If the plan includes public API breaks, package renames that affect consumers, or a large risky refactor, stop for user approval. Otherwise continue.
Set phase: "initialize".
7. Repository Initialization
7.1 Create destination repo
Create a private destination repository with gh:
gh repo create "$DESTINATION_OWNER/$DESTINATION_NAME" --privateIf the repo already exists, verify it is private, empty, and explicitly approved by the user before using it.
7.2 Reset history
Remove upstream Git history and initialize fresh:
rm -rf .git
git init
git branch -M "$DESTINATION_DEFAULT_BRANCH"
git remote add origin "git@github.com:$DESTINATION_OWNER/$DESTINATION_NAME.git"Do not fork. Do not preserve upstream commits. The initial push must contain exactly one fresh commit. Subsequent CI repairs may add focused commits; never rewrite published history merely to restore a one-commit count.
Set phase: "sanitize".
8. Identity Sanitization
Remove or replace source identity everywhere legally allowed.
Search and update README/docs, comments, package metadata, import/module paths, CLI names/help, env vars/config namespaces, CI names/badges, Docker images, release artifacts, snapshots/examples, generated docs, Git remotes, and package registry metadata.
No final commit message, docs page, code file, badge, package manifest, or config file may mention the upstream repo unless legally required and approved.
Validation search:
rg -n "$SOURCE_OWNER|$SOURCE_REPO|github.com/$SOURCE_OWNER/$SOURCE_REPO" .
git remote -vSet phase: "restructure".
9. Structure Cleanup
9.1 Flat directories
For each flat directory found in the audit:
- Decide whether flatness is idiomatic for the language/framework.
- If it mixes responsibilities, group files into cohesive subdirectories.
- Update imports, package configs, tests, docs, and scripts.
- Preserve public APIs unless the user approved a breaking change.
Avoid cosmetic churn. Reorganize only when navigation or maintainability improves.
9.2 Files over 1000 LOC
For each file over 1000 LOC:
- Skip generated, vendored, fixture, data, lock, or checked-in build artifact files unless there is a specific reason to change them.
- Split implementation files by responsibility, not arbitrary line count.
- Keep exported behavior stable.
- Add or update tests for risky seams.
- Document any intentionally remaining >1000 LOC files.
Set phase: "document".
10. Documentation
Create detailed user-facing and contributor-facing docs.
10.1 Root README
Keep the root README slim: project purpose, high-level capabilities, a working
quick start, technology/runtime badges, CI/release/license badges, and a link to
docs/. Include a subproject table only for a monorepo. Link badges to real
destination workflows, releases, and tools; omit unsupported claims.
Put detailed installation, configuration, usage, troubleshooting, architecture,
and contributor guidance in focused pages under docs/, with docs/README.md
as their index. Organize around user tasks, split large topics into linked
subdirectories, and verify examples against actual behavior. Include Mermaid
diagrams for meaningful architecture or workflow explanations.
10.2 Subproject READMEs
Every app/package/crate/subproject in a monorepo gets a README with name, purpose, useful badges, install/build, usage, development/testing, relationship to the broader repo, and a root docs link.
10.3 Contributor docs
Add or improve CONTRIBUTING.md or docs/development.md,
docs/architecture.md, docs/testing.md, docs/ci.md, docs/releasing.md
when releases exist, and AGENTS.md when agent guidance is useful.
Use Mermaid diagrams where they clarify architecture, development workflow, CI, state machines, or release flow. Do not add diagrams where prose is clearer.
Set phase: "harden".
11. Documentation and Budget Hardening
Make the migrated repo crisp and enforceable before generic tooling work.
11.1 README and docs
- Slim the root README into a scannable entry point: concise description,
destination badges, quick start,
docs/index, and license. - Move long architecture, API, operations, release, configuration, and contribution detail into focused docs or subproject READMEs.
- Normalize docs filenames to lowercase kebab-case with
git mv, then update links, docs nav, package metadata, tests, and examples. Conventional required files may stay uppercase:README.md,LICENSE,CHANGELOG.md,CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md,AGENTS.md, and framework-required files. - Add Mermaid diagrams only where they clarify architecture boundaries, request/data flow, development workflow, CI/release flow, or state machines.
- Add destination-repo badges for CI, release/version, runtimes, Flox, and license where useful. Remove stale upstream badges.
11.2 LOC budgets and deflatting
- Use the
loc-budgetskill when available, or implement its equivalent: file-size checker, flat-directory checker, budget data files, justified exceptions, and pre-commit/CI wiring. - Use the destination repo's idiomatic tooling: Python, Node/Bun/Deno, Ruby, Go, Rust, shell, or an existing repo-native command.
- Record a LOC baseline and explicit budgets for handwritten code; prefer deleting duplication and unnecessary abstractions before splitting files. Track net LOC change and avoid satisfying budgets by minifying code or moving it to exclusions.
- Modularize the worst large-file hitters and reorganize mixed-responsibility flat directories before adding exceptions.
- Keep framework-conventional flat directories only when justified in the flat-directory budget file.
- Budget checks must pass before the final commit; temporary exceptions must be explicit and documented in the handoff.
Set phase: "tool".
12. Tooling, Flox, CI, and Hooks
12.1 CI
Configure CI for the destination repo.
Requirements:
- Use the runner provider selected at Intake; validate labels for every OS and architecture, including release jobs. Ask about platform-specific exceptions.
- Include applicable formatting, lint, typecheck, test, build, dependency/security, shell, Actions syntax, docs-link, LOC/flat-directory, and lockfile checks.
- Validate Markdown and every Mermaid block in Markdown using a pinned Mermaid parser or renderer; Markdown lint alone does not validate diagram syntax.
- Share check commands with local hooks and run them in Flox. Use frozen/locked dependency installs, cache keys tied to lockfiles, job timeouts, concurrency, pinned actions, and least-privilege permissions. Avoid duplicate checks/jobs.
12.2 Pre-commit
Configure hooks that invoke the same scripts, versions, configs, and Flox environment as CI, including Markdown, Mermaid, and budget checks. Fast checks run at commit time; expensive full suites can run at pre-push with a documented full local command. Publishing and credentials-dependent checks remain CI-only; document those exceptions. A missing tool must fail clearly, not skip a gate.
Document and verify pre-commit install, any pre-push installation, and
pre-commit run --all-files from a clean checkout.
12.3 Flox and Bun
Make Flox the shared development and CI toolchain: commit the environment manifest and lockfile, declare runtimes and validation/release tools, and expose consistent build, test, lint, docs, and budget commands. Document activation, hook setup, and running the same checks locally and in CI. Validate a clean activation and document platform limitations rather than silently bypassing it.
Prefer Bun for compatible JavaScript/TypeScript dependency installation, scripts, tests, and docs tooling. Check framework and lifecycle-script compatibility before migrating; retain required Node runtimes. Pin Bun through Flox, commit its lockfile, use frozen installs in CI, and remove obsolete lockfiles only after validating the migration. Do not introduce a JS toolchain solely to replace adequate native tooling.
12.4 Automatic releases
Implement .github/workflows/auto-release.yml and release.yml adapted to the
destination stack and artifacts:
- Trigger auto-release on successful completion of the named CI workflow for a push to the destination default branch, never a pull request or fork run.
- Check out and tag the successful run's exact
head_sha, not a newer untested branch head. Serialize tagging; fetch tags and reuse an existing semantic version tag on that SHA. Start atv0.1.0, then increment the latest stable patch version unless the project has an established versioning policy. - Explicitly dispatch
release.ymlwith the tag after pushing it: do not rely on a tag pushed withGITHUB_TOKENtriggering another workflow. Grant only the auto-release job the required contents/actions write permissions. - Support tag-push and manual tag-input release triggers. Validate the tag, build from its immutable commit, run preflight checks, package appropriate platform artifacts with checksums, and publish generated release notes.
- Serialize release runs by tag across both triggers. Verify expected assets and checksums; retries must reuse tags and complete missing release work, including dispatch recovery when tag creation succeeded on an earlier run.
- Adapt package names, version injection, targets, secrets, and badge URLs to the destination. Do not copy unrelated integrations or project references.
Document triggers, versioning, permissions, artifacts, and retry procedures in
docs/releasing.md; validate packaging locally without publishing.
Set phase: "validate".
13. Validation
Run every applicable local gate: format, lint, type check, unit/integration tests, build, docs build, pre-commit all files, workflow syntax validation, and release smoke checks.
Run repository audits:
rg -n "$SOURCE_OWNER|$SOURCE_REPO|github.com/$SOURCE_OWNER/$SOURCE_REPO" .
rg -n "runs-on:" .github/workflows
find docs -type f 2>/dev/null | rg '/[A-Z][^/]*$' || true
git log --oneline
git remote -vConfirm no disallowed upstream references/history remain, the destination remote is correct, README is slim and linked to detail docs, docs filenames are lowercase except conventional files, Mermaid diagrams are useful and valid, badges point to the destination repo, LOC/flat-directory budgets pass, >1000 LOC files and flat directories are improved or justified, CI uses the selected runner provider, the license matches the bundled asset exactly, and shared pre-commit/Flox checks and automatic releases are documented.
If a validation failure cannot be fixed, document it in the handoff and stop for user approval before committing.
Set phase: "commit".
14. Final Commit
On the first push, create exactly one fresh commit. On resume, inspect the remote and recorded SHA first; if already pushed, continue monitoring or add only necessary repair commits.
Requirements: conventional commit format, human-written message, no upstream
mention, no AI attribution, no Co-Authored-By, and no generated-by footer.
Recommended message:
feat: initialize private projectThen push:
git add -A
git commit -m "feat: initialize private project"
git log --oneline
git push -u origin HEADBefore the initial push, verify there is exactly one commit:
test "$(git rev-list --count HEAD)" -eq 114.1 Monitor CI and releases
After every push, discover runs for the pushed SHA and watch them to completion
with gh run watch <run-id> --exit-status. Also track the downstream auto-release
and release runs for that commit/tag; a green build alone is insufficient.
Record run IDs and the final SHA in state so monitoring can resume.
Inspect failed logs, fix actionable failures, run affected checks locally, push
focused repair commits, and repeat until all required CI and release checks are
green for the final SHA. Do not disable gates to obtain green status or force-push
over published commits/tags. If credentials, unavailable runners, or an external
outage prevents progress, report the exact blocker and run URLs; leave the phase
at commit and do not claim completion. Resume monitoring after resolution.
Set phase: "handoff" only after confirming green runs and expected release
artifacts.
15. Handoff
Report the destination URL, privacy, name, final SHA, initial-history confirmation, final CI/release run URLs, major identity/structure/refactor changes, README/docs hardening, lowercase docs renames, Mermaid diagrams, badges, LOC/flat-directory budget setup, CI/pre-commit/Flox setup, validation commands, and any approved legal notices or remaining exceptions.
Keep the response concise. Do not mention the source repository in the handoff unless required for legal or user-approved context.
Definition of Done
- User approved the new destination name.
- Private destination repository exists.
- Initial push has one fresh commit; any CI repair commits are identified.
- No upstream Git history remains.
- No disallowed upstream references remain.
- Required legal notices are preserved or approved.
- Root and contributor docs are complete.
- Root README is slim and links to deeper docs.
- Documentation filenames are lowercase except conventional required files.
- Mermaid diagrams are added where they clarify architecture or workflow.
- README badges point to the destination repo.
- Each monorepo subproject has a useful README.
- CI uses the selected Blacksmith or GitHub-hosted runners.
- Pre-commit hooks are configured and documented.
- Flox provides the shared local/CI toolchain; Bun is used where compatible.
- The destination license matches the bundled license byte-for-byte.
- Markdown and Mermaid validation run in CI and local hooks.
- Automatic releases are retry-safe and build the CI-validated commit.
- Required CI and release runs are green for the final SHA.
- LOC and flat-directory budget gates are installed or equivalent gates exist.
- Flat directories and >1000 LOC files are addressed or justified in budgets.
- Validation passes or exceptions are explicitly approved.